1、定義html
平衡二叉樹,又稱AVL樹,它是一種特殊的二叉排序樹。AVL樹或者是一棵空樹,或者是具備如下性質的二叉樹:node
(1)左子樹和右子樹都是平衡二叉樹;ios
(2)左子樹和右子樹的深度(高度)之差的絕對值不超過1。ide
2、AVL樹的C++實現函數
一、結點的定義工具
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) {} };
二、AVL樹的操做post
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); };
三、旋轉url
在進行插入和刪除以前須要先了解AVL樹的旋轉操做。旋轉操做主要包括LL(左左)旋轉、LR(左右)旋轉、RR(右右)旋轉、RL(右左)旋轉,LL旋轉與RR旋轉對稱,LR旋轉與RL旋轉對稱。旋轉操做是在插入結點或刪除結點致使原AVL樹不平衡時進行的。個人理解是當二叉樹失衡的緣由出如今「最低失衡根結點左子樹的左子樹」(所謂「最低失衡根結點」,則是重新增結點開始向根部回溯,所遇到的第一個失衡的根節點)時,則使用LL旋轉來調整;當失衡出如今「最低失衡根節點左子樹的右子樹」,則使用LR旋轉調整;RR旋轉,RL旋轉同理。具體的定義和操做能夠看skywang12345的的文章:AVL樹(二)之 C++的實現(個人這篇文章就是基於此文章,爲了加深印象,在這裏把實現再寫一遍,加一些本身的理解)。spa
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.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; }
3、測試代碼
一、頭文件 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 }
二、源文件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 }
三、運行結果
先序遍歷: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樹
4、小工具
這裏分享一個二叉排序樹的可視化小工具,來自http://www.cnblogs.com/bbvi/p/5104916.html。
5、參考