• 平衡树


    平衡树中的AVL树
    平衡树是计算机科学中的一类数据结构。 平衡树是计算机科学中的一类改进的二叉查找树。一般的二叉查找树的查询复杂度是跟目标结点到树根的距离(即深度)有关,因此当结点的深度普遍较
    大时,查询的均摊复杂度会上升,为了更高效的查询,平衡树应运而生了。

     在这里,平衡指所有叶子的深度趋于平衡,更广义的是指在树上所有可能查找的均摊复杂度偏低。

     几乎所有平衡树的操作都基于树操作,通过旋转操作可以使得树趋于平衡。 对一棵查找树(search tree)进行查询/新增/删除 等动作, 所花的时间与树的高度h 成比例, 并不与树的容量n成比例。如果可以让树维持矮矮胖胖的好身材, 也就是让h维持在O(lg n)左右, 完成上述工作就很省时间。能够一直维持好身材, 不因新增删除而长歪的搜寻树, 叫做balanced search tree(平衡树)。 旋转Rotate —— 不破坏左小右大特性的小手术 平衡树有很多种, 其中有几类树维持平衡的方法, 都是靠整形小手术。

      各种平衡树:AVL树,经典平衡树,所有操作的最坏复杂度是O(lgN)的。

            Treap,利用随机堆的期望深度来优化树的深度,达到较优的期望复杂度。

            伸展树、红黑树,节点大小平衡树。2-3树、AA树。

     本篇博客主要描述AVL树

     AVL树:

      AVL树是一棵自平衡的二叉搜索树,在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下都是O(lgN)

    为什么需要AVL树:

      大多数二叉查找操作(搜索、最大、最小、插入、删除...)会花费O(h),h是二叉搜索树的高度。对于不平衡的二叉查找树,这些操作的时间复杂度为O(n)。如果我们保证在每一次插入和删除之后树的高度为O(lgN),那么我们就能保证对于所有的操作都有O(lgN)的上界。AVL树的高度总是O(logN),n是树中节点的数量。

    旋转

    如果在AVL树中进行插入或删除节点后,可能导致AVL树失去平衡。这种失去平衡的可以概括为4种姿态:LL(左左),LR(左右),RR(右右)和RL(右左)。下面给出它们的示意图:



    上图中的4棵树都是"失去平衡的AVL树",从左往右的情况依次是:LL、LR、RL、RR。除了上面的情况之外,还有其它的失去平衡的AVL树,如下图:

    上面的两张图都是为了便于理解,而列举的关于"失去平衡的AVL树"的例子。总的来说,AVL树失去平衡时的情况一定是LL、LR、RL、RR这4种之一,它们都由各自的定义:

    (1) LL:LeftLeft,也称为"左左"。插入或删除一个节点后,根节点的左子树的左子树还有非空子节点,导致"根的左子树的高度"比"根的右子树的高度"大2,导致AVL树失去了平衡。例如,在上面LL情况中,由于"根节点(8)的左子树(4)的左子树(2)还有非空子节点",而"根节点(8)的右子树(12)没有子节点";导致"根节点(8)的左子树(4)高度"比"根节点(8)的右子树(12)"高2。

    (2) LR:LeftRight,也称为"左右"。插入或删除一个节点后,根节点的左子树的右子树还有非空子节点,导致"根的左子树的高度"比"根的右子树的高度"大2,导致AVL树失去了平衡。例如,在上面LR情况中,由于"根节点(8)的左子树(4)的右子树(6)还有非空子节点",而"根节点(8)的右子树(12)没有子节点";导致"根节点(8)的左子树(4)高度"比"根节点(8)的右子树(12)"高2。

    (3) RL:RightLeft,称为"右左"。插入或删除一个节点后,根节点的右子树的左子树还有非空子节点,导致"根的右子树的高度"比"根的左子树的高度"大2,导致AVL树失去了平衡。例如,在上面RL情况中,由于"根节点(8)的右子树(12)的左子树(10)还有非空子节点",而"根节点(8)的左子树(4)没有子节点";导致"根节点(8)的右子树(12)高度"比"根节点(8)的左子树(4)"高2。

    (4) RR:RightRight,称为"右右"。插入或删除一个节点后,根节点的右子树的右子树还有非空子节点,导致"根的右子树的高度"比"根的左子树的高度"大2,导致AVL树失去了平衡。例如,在上面RR情况中,由于"根节点(8)的右子树(12)的右子树(14)还有非空子节点",而"根节点(8)的左子树(4)没有子节点";导致"根节点(8)的右子树(12)高度"比"根节点(8)的左子树(4)"高2。

    (1)LL的旋转

    LL失去平衡的情况,可以通过一次旋转让AVL树恢复平衡。如下图:

    图中左边是旋转之前的树,右边是旋转之后的树。从中可以发现,旋转之后的树又变成了AVL树,而且该旋转只需要一次即可完成。
    对于LL旋转,你可以这样理解为:LL旋转是围绕"失去平衡的AVL根节点"进行的,也就是节点k2;而且由于是LL情况,即左左情况,就用手抓着"左孩子,即k1"使劲摇。将k1变成根节点,k2变
    成k1的右子树,"k1的右子树"变成"k2的左子树"。

    (2)RR的旋转

    理解了LL之后,RR就相当容易理解了。RR是与LL对称的情况!RR恢复平衡的旋转方法如下:


    图中左边是旋转之前的树,右边是旋转之后的树。RR旋转也只需要一次即可完成。

    (3) LR的旋转

    LR失去平衡的情况,需要经过两次旋转才能让AVL树恢复平衡。如下图:



    第一次旋转是围绕"k1"进行的"RR旋转",第二次是围绕"k3"进行的"LL旋转"。

    (4)RL的旋转

    RL是与LR的对称情况!RL恢复平衡的旋转方法如下:

    第一次旋转是围绕"k3"进行的"LL旋转",第二次是围绕"k1"进行的"RR旋转"。

    插入:

      向AVL树插入,可以透过如同它是未平衡的二叉查找树一样,把给定的值插入树中,接着自底往上向根节点折回,于在插入期间成为不平衡的所有节点上进行旋转来完成。因为折回到根节点的路途上最多有1.44乘log n个节点,而每次AVL旋转都耗费固定的时间,所以插入处理在整体上的耗费为O(log n) 时间。

     删除:

      从AVL树中删除,可以通过把要删除的节点向下旋转成一个叶子节点,接着直接移除这个叶子节点来完成。因为在旋转成叶子节点期间最多有log n个节点被旋转,而每次AVL旋转耗费固定的时间,所以删除处理在整体上耗费O(log n) 时间。

     查找

      可以像普通二叉查找树一样的进行,所以耗费O(log n)时间,因为AVL树总是保持平衡的。不需要特殊的准备,树的结构不会由于查找而改变。



    头文件:AVLTree.h
    1
    #pragma once 2 template<class K,class V> 3 struct AVLTreeNode 4 { 5 AVLTreeNode<K, V>* _left; 6 AVLTreeNode<K, V>* _right; 7 AVLTreeNode<K, V>* _parent; 8 K _key; 9 V _value; 10 int _bf; 11 AVLTreeNode(const K&key,const V&value) 12 :_key(key), _value(value), _bf(0), _left(NULL), _right(NULL), _parent(NULL) 13 {} 14 }; 15 16 17 18 template<class K,class V> 19 20 class AVLTree 21 22 { 23 24 typedef AVLTreeNode<K, V> Node; 25 26 public: 27 28 AVLTree() :_root(NULL) 29 30 {} 31 32 bool Insert(const K&key, const V&value) 33 34 { 35 36   if (_root == NULL) 37 38    { 39 40     _root = new Node(key, value); 41 42      return true; 43 44   } 45 46   Node* parent = NULL; 47 48   Node* cur = _root; while (cur) 49 50    { 51 52      if (cur->_key > key) 53 54     { 55 56       parent = cur; 57 58       cur = cur->_left; 59 60      } 61 62     else if (cur->_key < key) 63 64     { 65 66        parent = cur; 67 68        cur = cur->_right; 69 70     } 71 72     else 73 74     { 75 76       return false; 77 78      } 79 80 } 81 82 cur = new Node(key, value); 83 if (parent->_key < key) 84 { 85    parent->_right = cur; 86    cur->_parent = parent; 87 } 88 else 89 { 90    parent->_left = cur; 91    cur->_parent = parent; 92 } 93 bool isRotate = false; 94 while (parent) 95 { 96    if (parent->_left == cur) 97   { 98      parent->_bf--; 99   } 100    else 101    { 102      parent->_bf++; 103   } 104    if (parent->_bf == 0) 105   { 106     break; 107    } 108   else if (parent->_bf == 1 || parent->_bf == -1) 109    { 110      cur = parent;//回溯继续调整平衡因子 111      parent = cur->_parent; 112    } 113    else 114    { 115      isRotate = true; 116      if (parent->_bf == 2) 117      { 118       if (cur->_bf == 1) 119        { 120          _RotateL(parent); 121        } 122        else 123        { 124          _RotateRL(parent); 125        } 126      } 127      else 128     { 129        if (cur->_bf == -1) 130        { 131          _RotateR(parent); 132        } 133        else 134        { 135          _RotateLR(parent); 136        } 137     } 138      break; 139   } 140 } 141 142 if (isRotate) 143 { 144   Node* ppNode = parent->_parent; 145    if (ppNode == NULL) 146    { 147      _root = parent; 148   } 149   else 150   { 151      if (ppNode->_key < parent->_key) 152      { 153        ppNode->_right = parent; 154      } 155      else 156      { 157        ppNode->_left = parent; 158      } 159   } 160    return true; 161 } 162 } 163 164 bool IsBalance() 165 166 { 167 168     return _IsBalance(_root); 169 170 } 171 172 int Height() 173 174 { 175 176     return _Height(_root); 177 178 } 179 180 void Inorder() 181 182 { 183 184     return _Inorder(_root); 185 186 } 187 188 protected: 189 void _RotateL(Node*& parent) 190 { 191    Node* subR = parent->_right; 192    Node* subRL = subR->_left; 193   parent->_right = subRL; 194    if (subRL) 195   { 196      subRL->_parent = parent; 197    } 198   subR->_left = parent; 199    subR->_parent = parent->_parent; 200   parent->_parent = subR; 201   parent->_bf = subR->_bf = 0; 202   parent = subR; 203 } 204 205 void _RotateR(Node*& parent) 206 { 207   Node* subL = parent->_left; 208   Node* subLR = subL->_right; 209   parent->_left = subLR; 210   if (subLR) 211   { 212      subLR->_parent = parent; 213    } 214    subL->_right = parent; 215    subL->_parent = parent->_parent; 216   parent->_parent = subL; 217    parent->_bf = subL->_bf = 0; 218    parent = subL; 219 } 220 221 222 void _RotateLR(Node*& parent) 223 { 224   Node* subL = parent->_left; 225    Node* subLR = subL->_right; 226   subL->_right = subLR->_left; 227   if (subLR->_left) 228    { 229      subLR->_left->_parent = subL; 230   } 231    subLR->_left = subL; 232    subLR->_parent = subL->_parent; 233    subL->_parent = subLR; 234    if (subLR->_bf == 0 || subLR->_bf == -1) 235    { 236      subL->_bf = 0; 237   } 238   else 239   { 240      subL->_bf = -1; 241    } 242 243    parent->_left = subLR->_right; 244 245   if (subLR->_right) 246 247   { 248 249     subLR->_right->_parent = parent; 250 251   } 252 253   subLR->_right = parent; 254 255   subLR->_parent = parent->_parent; 256 257   parent->_parent = subLR; 258 259   if (subLR->_bf == 0 || subLR->_bf ==1 ) 260 261   { 262 263     parent->_bf = 0; 264 265   } 266 267   else 268 269   { 270 271     parent->_bf = 1; 272 273   } 274 275   subLR->_bf = 0; 276 277   parent = subLR; 278 279 } 280 281 void _RotateRL(Node*& parent) 282 { 283    Node* subR = parent->_right; 284    Node* subRL = subR->_left; 285   subR->_left = subRL->_right; 286    if (subRL->_right) 287   { 288      subRL->_right->_parent = subR; 289   } 290   subRL->_right = subR; 291   subRL->_parent = subR->_parent; 292   subR->_parent = subRL; 293   if (subRL->_bf == 0 || subRL->_bf == 1) 294   { 295      subR->_bf = 0; 296   } 297    else 298   { 299      subR->_bf = 1; 300    } 301    parent->_right = subRL->_left; 302    if (subRL->_left) 303   { 304     subRL->_left->_parent = parent; 305   } 306   subRL->_left = parent; 307   subRL->_parent= parent->_parent; 308    parent->_parent = subRL; 309    if (subRL->_bf == 0 || subRL->_bf == -1) 310   { 311      parent->_bf = 0; 312    } 313    else 314   { 315      parent->_bf = -1; 316    } 317   subRL->_bf = 0; 318   parent = subRL; 319 } 320 321 int _Height(Node* root) 322 { 323   if (root == NULL) 324      return 0; 325   int left = _Height(root->_left) + 1; 326    int right = _Height(root->_right) + 1; 327   return left > right ? left : right; 328 } 329 330 bool _IsBalance(Node* root) 331 { 332   if (root == NULL) 333    { 334      return true; 335    } 336    int left = _Height(root->_left); 337    int right = _Height(root->_right); 338    int bf = abs(right - left); 339    if (bf > 1) 340    { 341      return false; 342    } 343    if (bf != abs(root->_bf)) 344    { 345      cout << root->_key << "平衡因子有问题"; 346      return false; 347    } 348   return _IsBalance(root->_left) && _IsBalance(root->_right); 349 } 350 351 void _Inorder(Node* root) 352 { 353   if (root == NULL) 354   { 355      return; 356   } 357   else 358    { 359      _Inorder(root->_left); 360     cout << root->_key << " "; 361     _Inorder(root->_right); 362   }   363 } 364 365 protected: 366 367   Node* _root; 368 369 }; 370 371 void Test() 372 373 {   374 375    AVLTree<int, int> t; 376 377   int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 }; 378 379   for (size_t i = 0; i < sizeof(a) / sizeof(int); ++i) 380 381   { 382 383     t.Insert(a[i], i); 384 385   } 386 387   t.Inorder(); 388 389   cout << endl; 390 391   cout << t.IsBalance()<< endl; 392 393 } 394 395
  • 相关阅读:
    Linux学习32
    Linux学习31-如何查看 linux 系统是centos还是ubuntu,并查看系统版本号
    python测试开发django-124.bootstrap点删除按钮弹确认删除
    python测试开发django-123.bootstrap模态框(modal)垂直居中显示
    python测试开发django-122.bootstrap模态框(modal)学习
    Linux下查询tomcat进程命令
    linux服务器通过X11实现图形化界面显示 1 背景描述
    linux(centos)配置ipv6网卡
    linux 下给网卡添加ipv6、路由
    2021年最新大厂php+go面试题集(1)
  • 原文地址:https://www.cnblogs.com/yuanshuang/p/5426387.html
Copyright © 2020-2023  润新知