zoukankan      html  css  js  c++  java
  • 平衡二叉树

    参考: 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 }
  • 相关阅读:
    微信小程序中的WXS语法
    小程序 没有找到可以构建的 NPM 包,请确认需要参与构建的 npm 都在 `miniprogramRoot` 目录内
    Linux 远程连接sftp与ftp
    MySQL使用方法
    Rsync+Inotify实时同步环境部署
    inotify+rsync实时同步
    rsync备份
    C# 基于ef的2种简单的仓储封装和工作单元 net core3.1 ---sqlserver 2019
    xshell个人免费版下载使用
    Git 命令之 git add、git commit
  • 原文地址:https://www.cnblogs.com/talenth/p/6880094.html
Copyright © 2011-2022 走看看