平衡二叉树(AVL树)
一、定义
平衡二叉树,又称AVL树,它是一种特殊的二叉排序树。AVL树或者是一棵空树,或者是具有以下性质的二叉树:
(1)左子树和右子树都是平衡二叉树;
(2)左子树和右子树的深度(高度)之差的绝对值不超过1。
二、AVL树的C++实现
1、结点的定义
class AVLNode { public: int key; //结点的值 int height; //结点的高度,根结点为0 AVLNode* left; //左孩子 AVLNode* right; //右孩子 /*构造函数*/ AVLNode(int k, AVLNode* left, AVLNode* right) :key(k), height(0), left(left), right(right) {} };
2、AVL树的操作
AVL树同二叉排序树一样,有遍历(先序、中序、后序),最大值与最小值,插入和删除,销毁二叉树等操作,除插入和删除与二叉排序树操作不同之外,其余均与二叉排序树相同,所以这里就只写AVL插入和删除操作。
class AVLTree { private: AVLNode* root; //根节点 public: /*构造函数*/ AVLTree() :root(NULL) {}; /*返回根节点*/ AVLNode* getRoot() { return root; } /*先序遍历*/ void preOrder(AVLNode* root); /*中序遍历*/ void inOrder(AVLNode* root); /*后序遍历*/ void postOrder(AVLNode* root); /*在AVL树root中查找值为key的结点并返回该结点*/ AVLNode* search(AVLNode* root, int key); /*在AVL树中查找最小值结点并返回*/ AVLNode* minimus(AVLNode* node); /*在AVL树中查找最大值结点并返回*/ AVLNode* maximus(AVLNode* node); /*返回结点的高度*/ int height(AVLNode* node); /*左左旋转*/ AVLNode* leftLeftRotate(AVLNode* root); /*右右旋转*/ AVLNode* rightRightRotate(AVLNode* root); /*左右旋转*/ AVLNode* leftRightRotate(AVLNode* root); /*右左旋转*/ AVLNode* rightLeftRotate(AVLNode* root); /*插入结点*/ AVLNode* insert(AVLNode* root, int key); /*删除结点node*/ AVLNode* deleteNode(AVLNode* root, AVLNode* node); /*销毁AVL树*/ void destroy(AVLNode* root); };
3、旋转
在进行插入和删除之前需要先了解AVL树的旋转操作。旋转操作主要包括LL(左左)旋转、LR(左右)旋转、RR(右右)旋转、RL(右左)旋转,LL旋转与RR旋转对称,LR旋转与RL旋转对称。旋转操作是在插入结点或删除结点导致原AVL树不平衡时进行的。我的理解是当二叉树失衡的原因出现在“最低失衡根结点左子树的左子树”(所谓“最低失衡根结点”,则是从新增结点开始向根部回溯,所遇到的第一个失衡的根节点)时,则使用LL旋转来调整;当失衡出现在“最低失衡根节点左子树的右子树”,则使用LR旋转调整;RR旋转,RL旋转同理。具体的定义和操作可以看skywang12345的的文章:AVL树(二)之 C++的实现(我的这篇文章就是基于此文章,为了加深印象,在这里把实现再写一遍,加一些自己的理解)。
3.1 LL旋转
如上图所示,找到“最低失衡根结点”,上图是结点5,二叉树失衡的原因是因为结点1的存在,而结点1位于结点5“左子树的左子树”,所以要使用LL旋转来调节,只需一次旋转即可达到平衡。具体的方法是:LL旋转的对象是“最低失衡根结点”,也就是结点5,找到5的左孩子3,将3的右孩子4变成5的左孩子,最后将5变成3的右孩子,调整后的AVL树如下所示:
具体代码:
/*LL旋转, * 参数: * root : 失衡AVL树根节点 * 返回值 : 调整后的AVL树根节点 */ AVLNode* AVLTree::leftLeftRotate(AVLNode* root) { AVLNode* lchild = root->left; root->left = lchild->right; lchild->right = root; lchild->height = max(height(lchild->left), height(root)) + 1; root->height = max(height(root->left), height(root->right)) + 1; return lchild; }
3.2 RR旋转
RR旋转与LL旋转对称。
如上图所示,“最低失衡根结点”是结点2,二叉树的失衡是结点6导致的,而结点6位于结点2“右子树的右子树”,所以要使用RR旋转来调节,只需一次旋转即可达到平衡。方法与LL旋转类似:RR旋转的对象是“最低失衡根结点”,这里是结点2,找到2的右孩子4,将4的左孩子3变成2的右孩子,最后将2变成4的右孩子,旋转后的结果如下图所示:
RR旋转代码如下:
/*RR旋转, * 参数: * root : 失衡AVL树根节点 * 返回值 : 调整后的AVL树根节点 */ AVLNode* AVLTree::rightRightRotate(AVLNode* root) { AVLNode* rchild = root->right; root->right = rchild->left; rchild->left = root; rchild->height = max(height(root), height(rchild->right)) + 1; root->height = max(height(root->left), height(root->right)) + 1; return rchild; }
3.3 LR旋转
LL旋转和RR旋转只需一次旋转即可达到平衡,而LR旋转和RL旋转需两次旋转才能达到平衡。
如上图所示,“最低失衡根结点”为结点5,二叉树失衡是因为结点3的存在,结点3位于结点5“左子树的右子树”,所以使用LR旋转来调节。方法:(1)先对最低失衡根结点的左孩子(结点2)进行RR旋转;(2)再对最低失衡根结点(结点5)进行LL旋转。下图演示了调整过程。
LR代码如下:
/*LR旋转 * 参数: * root : 失衡AVL树根节点 * 返回值 : 调整后的AVL树根节点 */ AVLNode* AVLTree::leftRightRotate(AVLNode* root) { root->left = rightRightRotate(root->left); //先对左子树右右旋转 return leftLeftRotate(root); //再对根结点左左旋转 }
3.4 RL旋转
RL旋转与LR旋转对称,先LL旋转,在RR旋转。
分析过程与LR相似。旋转步骤:(1)先对最低失衡结点右孩子(结点5)LL旋转;(2)在对最低失衡结点(结点2)RR旋转。旋转过程如下:
RL实现代码:
/*RL旋转 * 参数: * root : 失衡AVL树根节点 * 返回值 : 调整后的AVL树根节点 */ AVLNode* AVLTree::rightLeftRotate(AVLNode* root) { root->right = leftLeftRotate(root->right); return rightRightRotate(root); }
4、插入结点与删除结点
4.1 插入结点
插入操作与向二叉排序树中插入大体相同,只是多了插入结点后判断二叉树是否失衡以及失衡后的调整操作。
/* * 将结点插入到AVL树中,并返回根节点 * * 参数说明: * root 插入新结点前AVL树的根结点 * key 插入的结点的键值 * 返回值: * 插入结点后AVL树的根节点 */ AVLNode* AVLTree::insert(AVLNode* root, int key) { if (root == NULL) root = new AVLNode(key, NULL, NULL); else if (key < root->key) //插入左子树 { root->left = insert(root->left, key); if (height(root->left) - height(root->right) == 2) //插入导致二叉树失衡 { if (key < root->left->key) root = leftLeftRotate(root); else root = leftRightRotate(root); } } else if (key>root->key) //插入右子树 { root->right = insert(root->right, key); if (height(root->right) - height(root->left) == 2) //插入导致二叉树失衡 { if (key > root->right->key) root = rightRightRotate(root); else root = rightLeftRotate(root); } } root->height = max(height(root->left), height(root->right)) + 1; return root; }
4.2 删除结点
删除结点后要判断二叉树是否失衡,若失衡则进行调整操作。
/* * 将结点插入到AVL树中,并返回根节点 * * 参数说明: * root 删除结点前AVL树的根结点 * node 要删除的结点 * 返回值: * 删除结点node后AVL树的根节点 */ AVLNode* AVLTree::deleteNode(AVLNode* root, AVLNode* node) { if (root == NULL) return NULL; if (node->key < root->key) //要删除的结点在左子树 { root->left = deleteNode(root->left, node); if (height(root->right) - height(root->left) == 2) //删除导致二叉树失衡 { AVLNode* rightNode = root->right; if (height(rightNode->left)>height(rightNode->right)) root = rightLeftRotate(root); else root = rightRightRotate(root); } } else if (node->key > root->key) //要删除的结点在右子树 { root->right = deleteNode(root->right, node); if (height(root->left) - height(root->right) == 2) //删除导致二叉树失衡 { AVLNode* leftNode = root->left; if (height(leftNode->left) > height(leftNode->right)) root = leftLeftRotate(root); else root = leftRightRotate(root); } } else //找到了要删除的结点 { if (root->left != NULL&&root->right != NULL) //结点的左右子树均不为空 { if (height(root->left) > height(root->right)) { /* * 如果tree的左子树比右子树高; * 则(01)找出tree的左子树中的最大节点 * (02)将该最大节点的值赋值给tree。 * (03)删除该最大节点。 * 这类似于用"tree的左子树中最大节点"做"tree"的替身; * 采用这种方式的好处是:删除"tree的左子树中最大节点"之后,AVL树仍然是平衡的。 */ AVLNode* maxNode = maximus(root->left); root->key = maxNode->key; root->left = deleteNode(root->left, maxNode); } else { /* * 如果tree的左子树不比右子树高(即它们相等,或右子树比左子树高1) * 则(01)找出tree的右子树中的最小节点 * (02)将该最小节点的值赋值给tree。 * (03)删除该最小节点。 * 这类似于用"tree的右子树中最小节点"做"tree"的替身; * 采用这种方式的好处是:删除"tree的右子树中最小节点"之后,AVL树仍然是平衡的。 */ AVLNode* minNode = minimus(root->right); root->key = minNode->key; root->right = deleteNode(root->right, minNode); } } else { AVLNode* tmp = root; root = (root->left != NULL) ? root->left : root->right; delete tmp; } } return root; }
三、测试代码
1、头文件 avltree.h
1 #include <algorithm> 2 #include <iostream> 3 #include <cstdio> 4 #include <cstring> 5 using namespace std; 6 7 class AVLNode 8 { 9 public: 10 int key; //结点的值 11 int height; //结点的高度,根结点为0 12 AVLNode* left; //左孩子 13 AVLNode* right; //右孩子 14 15 /*构造函数*/ 16 AVLNode(int k, AVLNode* left, AVLNode* right) :key(k), height(0), left(left), right(right) {} 17 }; 18 19 class AVLTree 20 { 21 private: 22 AVLNode* root; //根节点 23 public: 24 /*构造函数*/ 25 AVLTree() :root(NULL) {}; 26 27 /*返回根节点*/ 28 AVLNode* getRoot() { return root; } 29 30 /*先序遍历*/ 31 void preOrder(AVLNode* root); 32 33 /*中序遍历*/ 34 void inOrder(AVLNode* root); 35 36 /*后序遍历*/ 37 void postOrder(AVLNode* root); 38 39 /*在AVL树root中查找值为key的结点并返回该结点*/ 40 AVLNode* search(AVLNode* root, int key); 41 42 /*在AVL树中查找最小值结点并返回*/ 43 AVLNode* minimus(AVLNode* node); 44 45 /*在AVL树中查找最大值结点并返回*/ 46 AVLNode* maximus(AVLNode* node); 47 48 /*返回结点的高度*/ 49 int height(AVLNode* node); 50 51 /*左左旋转*/ 52 AVLNode* leftLeftRotate(AVLNode* root); 53 54 /*右右旋转*/ 55 AVLNode* rightRightRotate(AVLNode* root); 56 57 /*左右旋转*/ 58 AVLNode* leftRightRotate(AVLNode* root); 59 60 /*右左旋转*/ 61 AVLNode* rightLeftRotate(AVLNode* root); 62 63 /*插入结点*/ 64 AVLNode* insert(AVLNode* root, int key); 65 66 /*删除结点node*/ 67 AVLNode* deleteNode(AVLNode* root, AVLNode* node); 68 69 /*销毁AVL树*/ 70 void destroy(AVLNode* root); 71 }; 72 73 /*先序遍历*/ 74 void AVLTree::preOrder(AVLNode* root) 75 { 76 if (root == NULL) 77 return; 78 cout << root->key << " "; 79 preOrder(root->left); 80 preOrder(root->right); 81 } 82 83 /*中序遍历*/ 84 void AVLTree::inOrder(AVLNode* root) 85 { 86 if (root == NULL) 87 return; 88 inOrder(root->left); 89 cout << root->key << " "; 90 inOrder(root->right); 91 } 92 93 /*后序遍历*/ 94 void AVLTree::postOrder(AVLNode* root) 95 { 96 if (root == NULL) 97 return; 98 postOrder(root->left); 99 postOrder(root->right); 100 cout << root->key << " "; 101 } 102 103 /*在AVL树root中查找值为key的结点并返回该结点*/ 104 AVLNode* AVLTree::search(AVLNode* root, int key) 105 { 106 if (root == NULL || root->key == key) 107 return root; 108 if (key < root->key) 109 search(root->left, key); 110 else search(root->right, key); 111 } 112 113 /*在AVL树中查找最小值结点并返回*/ 114 AVLNode* AVLTree::minimus(AVLNode* node) 115 { 116 if (node->left == NULL) 117 return node; 118 return minimus(node->left); 119 } 120 121 /*在AVL树中查找最大值结点并返回*/ 122 AVLNode* AVLTree::maximus(AVLNode* node) 123 { 124 if (node->right == NULL) 125 return node; 126 return maximus(node); 127 } 128 129 /*返回结点的高度*/ 130 int AVLTree::height(AVLNode* node) 131 { 132 if (node != NULL) 133 return node->height; 134 return 0; 135 } 136 137 138 /*LL旋转, 139 * 参数: 140 * root : 失衡AVL树根节点 141 * 返回值 : 调整后的AVL树根节点 142 */ 143 AVLNode* AVLTree::leftLeftRotate(AVLNode* root) 144 { 145 AVLNode* lchild = root->left; 146 root->left = lchild->right; 147 lchild->right = root; 148 149 lchild->height = max(height(lchild->left), height(root)) + 1; 150 root->height = max(height(root->left), height(root->right)) + 1; 151 152 return lchild; 153 } 154 155 /*RR旋转 156 * 参数: 157 * root : 失衡AVL树根节点 158 * 返回值 : 调整后的AVL树根节点 159 */ 160 AVLNode* AVLTree::rightRightRotate(AVLNode* root) 161 { 162 AVLNode* rchild = root->right; 163 root->right = rchild->left; 164 rchild->left = root; 165 166 rchild->height = max(height(root), height(rchild->right)) + 1; 167 root->height = max(height(root->left), height(root->right)) + 1; 168 169 return rchild; 170 } 171 172 /*LR旋转 173 * 参数: 174 * root : 失衡AVL树根节点 175 * 返回值 : 调整后的AVL树根节点 176 */ 177 AVLNode* AVLTree::leftRightRotate(AVLNode* root) 178 { 179 root->left = rightRightRotate(root->left); //先对左子树右右旋转 180 return leftLeftRotate(root); //再对根结点左左旋转 181 } 182 183 /*RL旋转 184 * 参数: 185 * root : 失衡AVL树根节点 186 * 返回值 : 调整后的AVL树根节点 187 */ 188 AVLNode* AVLTree::rightLeftRotate(AVLNode* root) 189 { 190 root->right = leftLeftRotate(root->right); 191 return rightRightRotate(root); 192 } 193 194 /* 195 * 将结点插入到AVL树中,并返回根节点 196 * 197 * 参数说明: 198 * root 插入新结点前AVL树的根结点 199 * key 插入的结点的键值 200 * 返回值: 201 * 插入结点后AVL树的根节点 202 */ 203 AVLNode* AVLTree::insert(AVLNode* root, int key) 204 { 205 if (root == NULL) 206 root = new AVLNode(key, NULL, NULL); 207 else if (key < root->key) //插入左子树 208 { 209 root->left = insert(root->left, key); 210 if (height(root->left) - height(root->right) == 2) //插入二叉树导致失衡 211 { 212 if (key < root->left->key) 213 root = leftLeftRotate(root); 214 else root = leftRightRotate(root); 215 } 216 } 217 else if (key>root->key) //插入右子树 218 { 219 root->right = insert(root->right, key); 220 if (height(root->right) - height(root->left) == 2) //插入导致二叉树失衡 221 { 222 if (key > root->right->key) 223 root = rightRightRotate(root); 224 else root = rightLeftRotate(root); 225 } 226 } 227 root->height = max(height(root->left), height(root->right)) + 1; 228 return root; 229 } 230 231 /* 232 * 将结点插入到AVL树中,并返回根节点 233 * 234 * 参数说明: 235 * root 删除结点前AVL树的根结点 236 * node 要删除的结点 237 * 返回值: 238 * 删除结点node后AVL树的根节点 239 */ 240 AVLNode* AVLTree::deleteNode(AVLNode* root, AVLNode* node) 241 { 242 if (root == NULL) 243 return NULL; 244 245 if (node->key < root->key) //要删除的结点在左子树 246 { 247 root->left = deleteNode(root->left, node); 248 if (height(root->right) - height(root->left) == 2) //删除导致二叉树失衡 249 { 250 AVLNode* rightNode = root->right; 251 if (height(rightNode->left)>height(rightNode->right)) 252 root = rightLeftRotate(root); 253 else root = rightRightRotate(root); 254 } 255 } 256 else if (node->key > root->key) //要删除的结点在右子树 257 { 258 root->right = deleteNode(root->right, node); 259 if (height(root->left) - height(root->right) == 2) //删除导致二叉树失衡 260 { 261 AVLNode* leftNode = root->left; 262 if (height(leftNode->left) > height(leftNode->right)) 263 root = leftLeftRotate(root); 264 else root = leftRightRotate(root); 265 } 266 } 267 else //找到了要删除的结点 268 { 269 if (root->left != NULL&&root->right != NULL) //结点的左右子树均不为空 270 { 271 if (height(root->left) > height(root->right)) 272 { 273 /* 274 * 如果tree的左子树比右子树高; 275 * 则(01)找出tree的左子树中的最大节点 276 * (02)将该最大节点的值赋值给tree。 277 * (03)删除该最大节点。 278 * 这类似于用"tree的左子树中最大节点"做"tree"的替身; 279 * 采用这种方式的好处是:删除"tree的左子树中最大节点"之后,AVL树仍然是平衡的。 280 */ 281 282 AVLNode* maxNode = maximus(root->left); 283 root->key = maxNode->key; 284 root->left = deleteNode(root->left, maxNode); 285 } 286 else 287 { 288 /* 289 * 如果tree的左子树不比右子树高(即它们相等,或右子树比左子树高1) 290 * 则(01)找出tree的右子树中的最小节点 291 * (02)将该最小节点的值赋值给tree。 292 * (03)删除该最小节点。 293 * 这类似于用"tree的右子树中最小节点"做"tree"的替身; 294 * 采用这种方式的好处是:删除"tree的右子树中最小节点"之后,AVL树仍然是平衡的。 295 */ 296 297 AVLNode* minNode = minimus(root->right); 298 root->key = minNode->key; 299 root->right = deleteNode(root->right, minNode); 300 } 301 } 302 else 303 { 304 AVLNode* tmp = root; 305 root = (root->left != NULL) ? root->left : root->right; 306 delete tmp; 307 } 308 } 309 return root; 310 } 311 312 /*销毁二叉树*/ 313 void AVLTree::destroy(AVLNode* root) 314 { 315 if (root == NULL) 316 return; 317 destroy(root->left); 318 destroy(root->right); 319 delete root; 320 }
2、源文件avltree.cpp
1 #include "avltree.h" 2 3 int main() 4 { 5 int a[] = { 3,2,1,4,5,6,7,16,15,14,13,12,11,10,8,9 }; 6 int len = sizeof(a) / sizeof(a[0]); 7 8 AVLTree* avlTree = new AVLTree(); 9 AVLNode* root = avlTree->getRoot(); 10 for (int i = 0;i < len;i++) 11 root = avlTree->insert(root, a[i]); 12 13 cout << "先序遍历:"; 14 avlTree->preOrder(root); 15 cout << endl; 16 17 cout << "中序遍历:"; 18 avlTree->inOrder(root); 19 cout << endl; 20 21 cout << "后序遍历:"; 22 avlTree->postOrder(root); 23 cout << endl; 24 25 cout << "删除结点4" << endl; 26 AVLNode* node = avlTree->search(root, 4); 27 if (node != NULL) 28 AVLNode* dnode = avlTree->deleteNode(root, node); 29 30 cout << "删除结点4后先序遍历:"; 31 avlTree->preOrder(root); 32 cout << endl; 33 cout << "删除结点4后中序遍历:"; 34 avlTree->inOrder(root); 35 cout << endl; 36 37 cout << "销毁AVL树" << endl; 38 avlTree->destroy(root); 39 return 0; 40 }
3、运行结果
先序遍历:7 4 2 1 3 6 5 13 11 9 8 10 12 15 14 16 中序遍历:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 后序遍历:1 3 2 5 6 4 8 10 9 12 11 14 16 15 13 7 删除结点4 删除结点4后先序遍历:7 5 2 1 3 6 13 11 9 8 10 12 15 14 16 删除结点4后中序遍历:1 2 3 5 6 7 8 9 10 11 12 13 14 15 16 销毁AVL树
四、小工具
这里分享一个二叉排序树的可视化小工具,来自http://www.cnblogs.com/bbvi/p/5104916.html。
五、参考