平衡二叉树
Posted talenth
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了平衡二叉树相关的知识,希望对你有一定的参考价值。
参考: http://www.cppblog.com/cxiaojia/archive/2012/08/20/187776.html
一 定义
平衡二叉树(Balanced Binary Tree)是二叉查找树的一个进化体,也是第一个引入平衡概念的二叉树。1962年,G.M. Adelson-Velsky 和 E.M. Landis发明了这棵树,所以它又叫AVL树。平衡二叉树要求对于每一个节点来说,它的左右子树的高度之差不能超过1,如果插入或者删除一个节点使得高度之差大于1,就要进行节点之间的旋转,将二叉树重新维持在一个平衡状态。这个方案很好的解决了二叉查找树退化成链表的问题,把插入,查找,删除的时间复杂度最好情况和最坏情况都维持在O(logN)。但是频繁旋转会使插入和删除牺牲掉O(logN)左右的时间,不过相对二叉查找树来说,时间上稳定了很多。
平衡二叉树实现的大部分过程和二叉查找树是一样的(学平衡二叉树之前一定要会二叉查找树),区别就在于插入和删除之后要写一个旋转算法去维持平衡.
二 难点操作
1 旋转
对于一棵AVL树,由于一次插入删除操作造成该树高度不平衡时,根节点的两棵子树的高度只能差2. 而不平衡的情况只能是以下四种:
1、6节点的左子树3节点高度比右子树7节点大2,左子树3节点的左子树1节点高度大于右子树4节点,这种情况成为左左。
2、6节点的左子树2节点高度比右子树7节点大2,左子树2节点的左子树1节点高度小于右子树4节点,这种情况成为左右。
3、2节点的左子树1节点高度比右子树5节点小2,右子树5节点的左子树3节点高度大于右子树6节点,这种情况成为右左。
4、2节点的左子树1节点高度比右子树4节点小2,右子树4节点的左子树3节点高度小于右子树6节点,这种情况成为右右。
从图2中可以可以看出,1和4两种情况是对称的,这两种情况的旋转算法是一致的,只需要经过一次旋转就可以达到目标,我们称之为单旋转。2和3两种情况也是对称的,这两种情况的旋转算法也是一致的,需要进行两次旋转,我们称之为双旋转。
(1) 单旋转
单旋转是针对于左左和右右这两种情况的解决方案,这两种情况是对称的,只要解决了左左这种情况,右右就很好办了。图3是左左情况的解决方案,节点k2不满足平衡特性,因为它的左子树k1比右子树Z深2层,而且k1子树中,更深的一层的是k1的左子树X子树,所以属于左左情况。
为使树恢复平衡,我们把k2变成这棵树的根节点,因为k2大于k1,把k2置于k1的右子树上,而原本在k1右子树的Y大于k1,小于k2,就把Y置于k2的左子树上,这样既满足了二叉查找树的性质,又满足了平衡二叉树的性质。
这样的操作只需要一部分指针改变,结果我们得到另外一颗二叉查找树,它是一棵AVL树,因为X向上一移动了一层,Y还停留在原来的层面上,Z向下移动了一层。使得根节点两棵子树的高度差小于2
(2) 双旋转
对于左右和右左这两种情况,单旋转不能使它达到一个平衡状态,要经过两次旋转。双旋转是针对于这两种情况的解决方案,同样的,这样两种情况也是对称的,只要解决了左右这种情况,右左就很好办了。图4是左右情况的解决方案,节点k3不满足平衡特性,因为它的左子树k1比右子树Z深2层,而且k1子树中,更深的一层的是k1的右子树k2子树,所以属于左右情况。
为使树恢复平衡,我们需要进行两步,第一步,把k1作为根,进行一次右右情况下的旋转操作,旋转之后就变成了左左情况,所以第二步再进行一次左左情况下的旋转操作,最后得到了一棵以k2为根的平衡二叉树。
2 插入
插入的方法和二叉查找树基本一样,区别是,插入完成后需要从插入的节点开始维护一个到根节点的路径,每经过一个节点都要维持树的平衡。维持树的平衡要根据高度差的特点选择不同的旋转算法。
3 删除
删除的方法也和二叉查找树的一致,区别是,删除完成后,需要从删除节点的父亲开始向上维护树的平衡一直到根节点。
下面是完整的代码展示
1 #include<iostream> 2 //AVL树节点信息 3 template<typename T> 4 class TreeNode 5 { 6 public: 7 TreeNode() : 8 lson_(NULL), rson_(NULL) 9 { 10 11 } 12 T data_;//值 13 TreeNode* lson_;//指向左儿子的地址 14 TreeNode* rson_;//指向右儿子的地址 15 }; 16 //AVL树类的属性和方法声明 17 template<typename T> 18 class AVLTree 19 { 20 public: 21 AVLTree(); 22 ~AVLTree(); 23 void insert(T x);//插入接口 24 TreeNode<T>* find(T x);//查找接口 25 void Delete(T x);//删除接口 26 void traversal();//遍历接口 27 28 private: 29 void insertpri(TreeNode<T>* &node, T x);//插入 30 void Deletepri(TreeNode<T>* &node, T x);//删除 31 32 TreeNode<T>* findpri(TreeNode<T>* node, T x);//查找 33 void insubtree(TreeNode<T>* node);//中序遍历 34 35 int height(TreeNode<T>* node);//求树的高度 36 37 void SingRotateLeft(TreeNode<T>* &k2);//左左情况下的旋转 38 void SingRotateRight(TreeNode<T>* &k2);//右右情况下的旋转 39 void DoubleRotateLR(TreeNode<T>* &k3);//左右情况下的旋转 40 void DoubleRotateRL(TreeNode<T>* &k3);//右左情况下的旋转 41 42 void Delete(TreeNode<T>* &root); 43 private: 44 TreeNode<T>* root_;//根节点 45 }; 46 template<typename T> 47 AVLTree<T>::AVLTree() 48 : root_(NULL) 49 { 50 } 51 template<typename T> 52 AVLTree<T>::~AVLTree() 53 { 54 Delete(root_); 55 } 56 //计算节点的高度 57 template<typename T> 58 int AVLTree<T>::height(TreeNode<T>* node) 59 { 60 if (node != NULL) 61 { 62 int lh = height(node->lson_); 63 int rh = height(node->rson_); 64 return lh >= rh ? (lh + 1) : (rh + 1); 65 } 66 return -1; 67 } 68 template<typename T> 69 void AVLTree<T>::Delete(TreeNode<T>* &root) 70 { 71 if (root->lson_) 72 { 73 Delete(root->lson_); 74 root->lson_ =nullptr; 75 } 76 if (root->rson_) 77 { 78 Delete(root->rson_); 79 root->rson_ = nullptr; 80 } 81
82
83 delete root; 84 root = nullptr; 85
86 } 87 //左左情况下的旋转 88 template<typename T> 89 void AVLTree<T>::SingRotateLeft(TreeNode<T>* &k2) 90 { 91 if (!k2) 92 return; 93 94 TreeNode<T> *k1 = k2->lson_; 95 if (k1) 96 { 97 k2->lson_ = k1->rson_; 98 k1->rson_ = k2; 99 k2 = k1; 100 } 101 102 } 103 //右右情况下的旋转 104 template<typename T> 105 void AVLTree<T>::SingRotateRight(TreeNode<T>* &k2) 106 { 107 if (!k2) 108 return; 109 110 TreeNode<T> *k1 = k2->rson_; 111 if (k1) 112 { 113 k2->rson_ = k1->lson_; 114 k1->lson_ = k2; 115 k2 = k1; 116 } 117 } 118 //左右情况的旋转 119 template<typename T> 120 void AVLTree<T>::DoubleRotateLR(TreeNode<T>* &k3) 121 { 122 if (!k3) 123 return; 124 125 SingRotateRight(k3->lson_); 126 SingRotateLeft(k3); 127 } 128 //右左情况的旋转 129 template<typename T> 130 void AVLTree<T>::DoubleRotateRL(TreeNode<T>* &k3) 131 { 132 if (!k3) 133 return; 134 135 SingRotateLeft(k3->rson_); 136 SingRotateRight(k3); 137 } 138 //插入 139 template<typename T> 140 void AVLTree<T>::insertpri(TreeNode<T>* &node, T x) 141 { 142 if (node == NULL)//如果节点为空,就在此节点处加入x信息 143 { 144 node = new TreeNode<T>(); 145 node->data_ = x; 146 } 147 else if (node->data_ >= x)//如果x小于节点的值,就继续在节点的左子树中插入x 148 { 149 insertpri(node->lson_, x); 150 if (2 == height(node->lson_) - height(node->rson_)) 151 { 152 if (x < node->lson_->data_) 153 SingRotateLeft(node); 154 else 155 DoubleRotateLR(node); 156 } 157 } 158 else//如果x大于节点的值,就继续在节点的右子树中插入x 159 { 160 insertpri(node->rson_, x); 161 if (2 == height(node->rson_) - height(node->lson_))//如果高度之差为2的话就失去了平衡,需要旋转 162 { 163 if (x > node->rson_->data_) 164 SingRotateRight(node); 165 else 166 DoubleRotateRL(node); 167 } 168 } 169 } 170 //插入接口 171 template<typename T> 172 void AVLTree<T>::insert(T x) 173 { 174 insertpri(root_, x); 175 int lh = height(root_); 176 } 177 //查找 178 template<typename T> 179 TreeNode<T>* AVLTree<T>::findpri(TreeNode<T>* node, T x) 180 { 181 if (!node)//如果节点为空说明没找到,返回NULL 182 return NULL; 183 if (node->data_ > x)//如果x小于节点的值,就继续在节点的左子树中查找x 184 return findpri(node->lson_, x); 185 else if (node->data_ < x)//如果x大于节点的值,就继续在节点的右子树中查找x 186 return findpri(node->rson_, x); 187 else//如果相等,就找到了此节点 188 return node; 189 } 190 //查找接口 191 template<typename T> 192 TreeNode<T>* AVLTree<T>::find(T x) 193 { 194 return findpri(root_, x); 195 } 196 //删除 197 template<typename T> 198 void AVLTree<T>::Deletepri(TreeNode<T>* &node, T x) 199 { 200 if (!node) 201 return;//没有找到值是x的节点 202 203 if (x < node->data_) 204 { 205 Deletepri(node->lson_, x);//如果x小于节点的值,就继续在节点的左子树中删除x 206 if (height(node->rson_) - height(node->lson_) == 2) 207 { 208 if (height(node->rson_->lson_)>height(node->rson_->rson_)) 209 DoubleRotateRL(node); 210 else 211 SingRotateRight(node); 212 } 213 } 214 else if (x > node->data_) 215 { 216 Deletepri(node->rson_, x);//如果x大于节点的值,就继续在节点的右子树中删除x 217 if (height(node->lson_) - height(node->rson_) == 2) 218 { 219 if (height(node->lson_->rson_) > height(node->lson_->lson_)) 220 DoubleRotateLR(node); 221 else 222 SingRotateLeft(node); 223 } 224 } 225 else//如果相等,此节点就是要删除的节点 226 { 227 if (node->lson_&&node->rson_)//此节点有两个儿子 228 { 229 TreeNode<T>* temp_fathor = node; 230 TreeNode<T>* temp = node->rson_;//temp指向节点的右儿子 231 while (temp->lson_)//找到右子树中值最小的节点 232 { 233 temp_fathor = temp; 234 temp = temp->lson_; 235 } 236 237 //把右子树中最小节点的值赋值给本节点 238 node->data_ = temp->data_; 239 delete temp_fathor->lson_; 240 temp_fathor->lson_ = nullptr; 241 242 if (2 == height(node->lson_) - height(node->rson_)) 243 { 244 if (height(node->lson_->rson_) > height(node->lson_->lson_)) 245 DoubleRotateLR(node); 246 else 247 SingRotateLeft(node); 248 } 249 } 250 else//此节点有1个或0个儿子 251 { 252 TreeNode<T>* temp = node; 253 if (node->lson_ == NULL)//有右儿子或者没有儿子 254 node = node->rson_; 255 else if (node->rson_ == NULL)//有左儿子 256 node = node->lson_; 257 delete(temp); 258 temp = NULL; 259 } 260 } 261 } 262 //删除接口 263 template<typename T> 264 void AVLTree<T>::Delete(T x) 265 { 266 Deletepri(root_, x); 267 } 268 //中序遍历函数 269 template<typename T> 270 void AVLTree<T>::insubtree(TreeNode<T>* node) 271 { 272 if (node == NULL) 273 return; 274 insubtree(node->lson_);//先遍历左子树 275 std::cout << node->data_ << " ";//输出根节点 276 insubtree(node->rson_);//再遍历右子树 277 } 278 //中序遍历接口 279 template<typename T> 280 void AVLTree<T>::traversal() 281 { 282 insubtree(root_); 283 }
以上是关于平衡二叉树的主要内容,如果未能解决你的问题,请参考以下文章