代碼的思想和圖片參考:好大學慕課浙江大學陳越、何欽銘的《數據結構》
我們首先介紹一下什么是二叉搜索樹和二叉平衡樹:
二叉搜索樹:一棵二叉樹,可以為空;如果不為空,滿足以下性質
1. 非空左子樹的所有鍵值小於其根結點的鍵值。
2. 非空右子樹的所有鍵值大於其根結點的鍵值。
3. 左、右子樹都是二叉搜索樹。
二叉搜索樹操作的特別函數:
Position Find( ElementType X, BinTree BST ):從二叉搜索樹BST
中查找元素X,返回其所在結點的地址,查找的次數取決於樹的高度
算法思想:
和二分查找類似,如果大於根元素往右邊找,小於根元素網左邊找,可以使用遞歸和非遞歸的方法實現
注:對尾遞歸,可以用循環來實現。
Position FindMin( BinTree BST ):從二叉搜索樹BST中查找並返回
最小元素所在結點的地址
算法思想:最小的元素在二叉搜索樹的最左分支的端節點上。一直向左進行遞歸查找即可
Position FindMax( BinTree BST ) :從二叉搜索樹BST中查找並返回
最大元素所在結點的地址
算法思想:最小的元素在二叉搜索樹的最右分支的端節點上。一直向右進行遞歸查找即可
BinTree Insert( ElementType X, BinTree BST )
算法思想:和Find的方法差不多,先找到需要插入的位置,然后進行插入
BinTree Delete( ElementType X, BinTree BST )
算法思想:分為三種情況
1.刪除的節點是葉子節點:直接刪除
2.刪除的節點只有一個孩子:刪除該節點,把該節點的唯一的子節點掛到父節點上
3.該節點是有兩個孩子的父節點:我們可以把兩個孩子的節點看做是有一個孩子的父節點,但是必須從其子節點找到元素來
替換他,下面是找元素替換的方法。
3.1查找該節點左子樹的最大元素,把最大元素的值給該節點,然后把那個左子樹最大元素的節點刪除
3.2查找該節點右子樹的最小元素,把最小元素的值給該節點,然后把那個右子樹最小元素的節點刪除
為什么需要找最大或者最小元素呢,因為這樣可以最大或者最小元素可以保證該節點只有一個子節點或者沒有節點
否則找到一個具有兩個節點的父節點,問題還是沒有解決。
平衡二叉樹:對於二叉搜索樹來說,搜索的次數取決於樹的高度,那么,由於元素的插入順序和規則的不同
那么所生成樹的形狀和高度也不相同。可以參考講義的十二個月份按照不同規則插入,造成二叉搜索樹的高度差異有很大
我們希望樹比較平衡,這樣平均查找次數就比較少
定義:
平衡因子(Balance Factor,簡稱BF):BF(T)=hl-hr,
hl和hr為左右子樹的高度
平衡二叉樹的定義(Balance Binary tree 或者稱為AVL樹):
1.空樹
2.任意節點左右子樹的高度差不超過1,即|BF|<=1
那么我們需要思考,n個節點的平衡二叉樹的高度為多少呢?能不能滿足我們的需求呢。
我可以找規律,我們指定只有一個節點時,高度為0,那么就有如下規律
平衡二叉樹的高度 平衡二叉樹需要的最少節點個數
0 1 A
1 2
2 4
... ...
具體的規律和證明參考講義,最終我們得到一個結論:給定節點為N的AVL樹的最大高度為O(Log2 N)
平衡二叉樹的調整問題:
為什么需要調整平衡兒茶樹呢?因為我們隊平衡二叉樹的插入和刪除操作,可能會破壞
樹的平衡性,所以我們需要對樹的平衡性進行調整。
平衡二叉樹的調整圖文解析
注:數字代表平衡因子
平衡二叉樹的調整
注:數字代表平衡因子
1 RR調整
調整的演示圖
上面的不平衡的搜索樹的發現者是Mar,麻煩節點是Nov,Nov在發現者的右子樹的右邊,因而叫做RR插入,需要進行RR旋轉(右單旋)
下面給出RR調整的通用的圖解:
2 LL調整
LL調整的演示圖:
發現者Mar,麻煩節點為Apr在發現者的左子樹的左邊,因而叫LL插入,需要LL旋轉
下面是LL調整的通解圖:
3 LR調整
LR調整的演示圖
發現節點是May,麻煩節點是Jan,Jan在May左子樹的右子樹中,因而叫做LR插入,需要進行LR旋轉
LR調整的通解圖:
4 RL調整
RL調整的演示圖:
發現節點是Aug,麻煩節點是Feb,Feb在Aug的右子樹的左子樹上面,因而叫做RL插入,需要進行RL旋轉
RL調整的通解圖:
下面是二叉搜索樹的相關操作的代碼:

1 #include<stdio.h> 2 #include<stdlib.h> 3 4 typedef int elementType; 5 6 /*define a binary tree*/ 7 typedef struct node{ 8 elementType element; 9 struct node *left; 10 struct node *right; 11 int treeHeight;/*為了平衡二叉樹計算方便,定義樹高*/ 12 }tre,*pTree; 13 14 /*二叉搜索樹的遞歸查找*/ 15 pTree find(elementType element,pTree tree){ 16 if(!tree){ 17 return NULL; 18 } 19 if(element<tree->element){ 20 /*查找元素比根元素小在左子樹中查找*/ 21 return find(element,tree->left); 22 }else if(element>tree->element){ 23 /*查找元素比根元素小在右子樹中查找*/ 24 return find(element,tree->right); 25 }else{/*找到該節點,返回*/ 26 return tree; 27 } 28 } 29 30 /*因為遞歸方法執行的效率低,而且上面的尾遞歸函數可以改寫為遞歸函數*/ 31 pTree find2(elementType element,pTree tree){ 32 pTree temp; 33 temp = tree; 34 while(temp){ 35 if(element<temp->element){ 36 temp=temp->left; 37 }else if(element>temp->element){ 38 temp=temp->right; 39 }else{ 40 return temp; 41 } 42 } 43 return NULL; 44 } 45 46 /*根據二叉搜索樹的特點,最小元素在最左邊的節點上面*/ 47 pTree getMinElement(pTree tree){ 48 if(tree){ 49 while(tree->left){ 50 tree=tree->left; 51 } 52 } 53 54 return tree; 55 } 56 57 /*獲取二叉搜索樹中的最大元素,最大元素的位置應該在最右邊的節點上*/ 58 pTree getMaxElement(pTree tree){ 59 if(tree){ 60 while(tree->right){ 61 tree = tree->right; 62 } 63 } 64 65 return tree; 66 } 67 68 /*二叉搜索樹的插入,知識遵循二叉搜索樹的性質,但是並沒有調節平衡*/ 69 pTree insert(pTree tree,elementType element){ 70 pTree temp; 71 if(!tree){ 72 tree = (pTree)malloc(sizeof(tre)); 73 tree->element=element; 74 tree->left=tree->right=NULL; 75 }else{/**/ 76 if(element<tree->element){ 77 tree ->left = insert(tree->left,element); 78 } 79 else if(element>tree->element){ 80 tree->right = insert(tree->right,element); 81 } 82 } 83 return tree; 84 } 85 86 /*非遞歸的二叉搜索樹的算法*/ 87 pTree insert2(pTree tree,elementType element){ 88 pTree temp; 89 int flag; 90 pTree parent; 91 if(!tree){ 92 tree = (pTree)malloc(sizeof(tre)); 93 tree->element=element; 94 tree->left=tree->right=NULL; 95 }else{ 96 temp=tree; 97 while(temp!=NULL){ 98 if(element<temp->element){ 99 //printf("lala\n"); 100 parent = temp; 101 temp=temp->left; 102 flag=0; 103 }else if(element>temp->element){ 104 parent = temp; 105 flag=1; 106 temp=temp->right; 107 } 108 } 109 110 temp = (pTree)malloc(sizeof(tre)); 111 temp->element=element; 112 temp->left=temp->right=NULL; 113 if(flag){ 114 parent->right=temp; 115 }else{ 116 parent->left=temp; 117 } 118 } 119 120 return tree; 121 } 122 123 /*在二叉搜索樹中刪除一個元素 124 算法思想: 125 1.首先查找到該元素 126 2.如果該元素是葉子節點,直接刪除 127 3.如果該元素有一個孩子節點,直接把孩子節點掛載到該節點的父節點上 128 4.如果該節點有兩個孩子,由兩種方法 129 a.在該節點的左子樹中找到最大元素節點T,把該節點的值替換成T的值,然后執行對T的刪除操作 130 b.在該節點的右子樹中找最小元素的節點T,把該節點的值替換為T的值,然后執行對T的刪除操作 131 注:找最大或者最小元素是因為最大最小元素是葉子節點或者只有一個孩子。 132 */ 133 pTree deleteElement(pTree tree,elementType element){ 134 pTree temp; 135 if(!tree){ 136 printf("the element don't search in this tree\n"); 137 }else if(element<tree->element){ 138 tree->left=deleteElement(tree->left,element); 139 }else if(element>tree->element){ 140 tree->right = deleteElement(tree->right,element); 141 }else{//找到需要刪除的元素節點 142 if(tree->left && tree->right){//該有兩個孩子節點 143 temp = getMinElement(tree->right);/*獲取右子樹的最小值節點*/ 144 tree->element=temp->element; 145 tree->right=deleteElement(tree->right,temp->element); 146 }else{ 147 temp=tree; 148 if(!tree->left){ 149 tree=tree->right; 150 }else if(!tree->right){ 151 tree=tree->left; 152 } 153 free(temp); 154 } 155 } 156 return tree; 157 } 158 159 /*使用非遞歸的方法 160 pTree deleteElement2(pTree tree,elementType element){ 161 pTree temp,maxSubNode,flag,temp2; 162 if(!tree){ 163 printf("the tree is empty,don't allow delete elememt\n"); 164 }else{ 165 temp = find(element,tree); 166 if(temp==NULL){ 167 printf("the element don't exsit in this tree\n"); 168 }else{ 169 if(temp->left && temp->right){ 170 maxSubNode = getMinElement(temp->right); 171 temp->element = maxSubNode->element; 172 }else{ 173 maxSubNode = temp; 174 } 175 176 temp2=maxSubNode; 177 if(!maxSubNode->left){ 178 maxSubNode=maxSubNode->right; 179 }else if(!maxSubNode->right){ 180 maxSubNode=maxSubNode->left; 181 } 182 free(temp2); 183 } 184 185 } 186 return tree; 187 }*/ 188 189 190 //先序遍歷 191 void preOrderTraversal(pTree tree){ 192 if(tree){ 193 printf("%d ",tree->element); 194 preOrderTraversal(tree->left); 195 preOrderTraversal(tree->right); 196 } 197 } 198 199 /*=====================================調整樹為平衡二叉樹===============================================*/ 200 201 int getMaxValue(int a,int b){ 202 return a > b ? a : b ; 203 } 204 205 /*獲取二叉樹的樹高*/ 206 int getHeight(pTree tree){ 207 int leftHeight,rightHeight; 208 if(tree){ 209 leftHeight = getHeight(tree->left); 210 rightHeight = getHeight(tree->right); 211 return (leftHeight>rightHeight ? leftHeight : rightHeight)+1; 212 } 213 return 0; 214 } 215 /* 216 左單旋操作:將A與B進行LL旋轉,並更新A和B的新高度,返回新的根節點B 217 A必須有一個左子節點B 218 */ 219 pTree singleLeftRatation(pTree A){ 220 pTree B = A->left; 221 A->left=B->right; 222 B->right = A; 223 A->treeHeight = getMaxValue(getHeight(A->left),getHeight(A->right))+1; 224 B->treeHeight = getMaxValue(B->left,A->treeHeight)+1; 225 return B; 226 } 227 228 /*右單旋:將A與B進行RR旋轉,並更新A與B的高度,返回新的根節點B 229 注:A必須有一個右節點B 230 */ 231 pTree singleRightRatation(pTree A){ 232 pTree B = A->right; 233 A->right = B->left; 234 B->left = A; 235 A->treeHeight = getMaxValue(getHeight(A->left),getHeight(A->right))+1; 236 B->treeHeight = getMaxValue(getHeight(B->right),A->treeHeight); 237 return B; 238 } 239 240 /* 241 將A做LR旋轉,返回新的根節點C 242 A必須有一個左自己的B,B必須有一個右子節點C 243 */ 244 pTree doubleLeftRightRatation(pTree A){ 245 /*先對B,C進行RR旋轉,C被返回*/ 246 A->left = singleRightRatation(A->left); 247 /*在對A和C進行LL旋轉,返回新的根節點C*/ 248 return singleLeftRatation(A); 249 } 250 251 /* 252 對A進行RL旋轉,返回新的根節點C 253 注:A必須有一個右子節點B,B必須有一個左子節點C 254 */ 255 pTree doubleRightLeftRatation(pTree A){ 256 /*先對B,C進行LL旋轉,返回新的根節點C*/ 257 A->right = singleLeftRatation(A->right); 258 /*在對A,C進行RR旋轉,返回新的根節點C*/ 259 return singleRightRatation(A); 260 } 261 262 /*對二叉搜索樹進行插入,插入后調整樹的平衡*/ 263 pTree AVLInsert(pTree tree,elementType element){ 264 if(!tree){ 265 tree = (pTree)malloc(sizeof(tre)); 266 tree->element = element; 267 tree->left=tree->right = NULL; 268 tree->treeHeight=0; 269 }else if(element<tree->element){ 270 tree->left = AVLInsert(tree->left,element); 271 //判斷平衡因子是否等於2 272 if(getHeight(tree->left)-getHeight(tree->right) == 2){ 273 if(element<tree->left->element){//element往tree的左子樹的左子樹插入導致平衡因子大於2,進行LL調整的 274 tree = singleLeftRatation(tree); 275 }else{//element往tree的左子樹的右子樹插入導致平衡因子大於2,進行LR調整 276 tree = doubleLeftRightRatation(tree); 277 } 278 } 279 }else if(element>tree->element){ 280 tree->right = AVLInsert(tree->right,element); 281 //判斷平衡因子是否等於2 282 if(getHeight(tree->right)-getHeight(tree->left) == 2){ 283 if(element>tree->right->element){//element往tree的右子樹的右子樹插入導致平衡因子大於2,進行RR調整 284 tree = singleRightRatation(tree); 285 }else{//element往tree的右子樹的左子樹插入導致平衡因子大於2,進行RL調整 286 tree = doubleRightLeftRatation(tree); 287 } 288 } 289 }/* else 如果找到了,就不進行插入*/ 290 291 tree->treeHeight = getMaxValue(getHeight(tree->left),(getHeight(tree->right)))+1; 292 return tree; 293 } 294 295 296 void main(){ 297 printf("\n==========普通插入=====================================\n"); 298 int findElement=33; 299 int deleteData=41; 300 pTree tree=insert(NULL,30); 301 tree=insert(tree,15); 302 tree=insert(tree,41); 303 tree=insert(tree,33); 304 tree=insert(tree,50); 305 tree=insert(tree,35); 306 preOrderTraversal(tree); 307 printf("\n"); 308 printf("The find element is:%d,the result is %d \n",findElement,find(findElement,tree)->element); 309 printf("The min element:%d\n",getMinElement(tree)->element); 310 printf("The max element:%d\n",getMaxElement(tree)->element); 311 //printf("delete the elemet %d\n",deleteData); 312 //deleteElement(tree,deleteData); 313 printf("\nordinary tree preOrder\n"); 314 preOrderTraversal(tree); 315 316 printf("\n==========AVL插入=====================================\n"); 317 318 pTree AVLTree=AVLInsert(NULL,30); 319 AVLTree=AVLInsert(AVLTree,15); 320 AVLTree=AVLInsert(AVLTree,41); 321 AVLTree=AVLInsert(AVLTree,33); 322 AVLTree=AVLInsert(AVLTree,50); 323 AVLTree=AVLInsert(AVLTree,35); 324 printf("\n"); 325 printf("The find element is:%d,the result is %d \n",findElement,find(findElement,AVLTree)->element); 326 printf("The min element:%d\n",getMinElement(AVLTree)->element); 327 printf("The max element:%d\n",getMaxElement(AVLTree)->element); 328 //printf("delete the elemet %d\n",deleteData); 329 //deleteElement(AVLTree,deleteData); 330 printf("\nAVL tree preOrder\n"); 331 preOrderTraversal(AVLTree); 332 333 }
依次插入 30 15 41 33 50 35
則
這個和程序運行的結果是一致的: