平衡树中的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