查找->動態查找表->平衡二叉樹
文字描述
平衡二叉樹(Balanced Binary Tree或Height-Balanced Tree)
因為是俄羅斯數學家G.M.Adel’son-Vel’skii和E.M.Landis在1962年提出來的,所以又稱AVL樹。它或者是一顆空樹,或者是具有下列性質的二叉樹:它的左子樹和右子樹都是平衡二叉樹,且左子樹和右子樹的深度之差的絕對值不超過1。若將二叉樹上結點的平衡因子BF(Balanced Factor)定義為該結點的左子樹的深度減去它的右子樹的深度,則平衡二叉樹上所有結點的平衡因子只可能是-1,0和1。只要二叉樹上有一個結點的平衡因子的絕對值大於1,則該二叉樹就是不平衡的。
那麽如何使二叉排序樹成為平衡樹呢?即在一顆二叉排序樹中因插入一個結點後失去平衡的話,怎麽調整才能使之重新平衡呢?
一般情況下,假設由於在二叉排序樹上插入結點而失去平衡的最小子樹根結點的指針a(即a是離插入結點最近,且平衡因子絕對值超過1的祖先結點),則失去平衡後進行調整的規律可歸納為下面4中情況:
(1)單向右旋平衡處理,圖9.13(a)所示:在*a的左子樹根結點的左子樹上插入結點後,*a的平衡因子由1增至2,致使以*a為根的子樹失去平衡,則需進行一次向右的順時針旋轉操作。
(2)雙向旋轉(先左後右),圖9.13(b)所示:在*a的左子樹根結點的右子樹上插入結點後,*a的平衡因子由1增至2,致使以*a為根的子樹失去平衡,則需進行兩次旋轉(先左旋後右旋)操作。
(3)單向左旋平衡處理,圖9.13(c)所示:在*a的右子樹根結點的右子樹上插入結點後,*a的平衡因子由-1變為-2,致使以*a為根的子樹失去平衡,則需進行一次向左的逆時針旋轉操作。
(4)雙向旋轉(先右後左),圖9.13(d)所示:在*a的右子樹根結點的左子樹上插入結點後,*a的平衡因子由-1變為-2,致使以*a為根的子樹失去平衡,則需進行兩次選擇(先右旋後左旋)
上訴4種情況中,(1)和(3)對稱,(2)和(4)對稱。它們旋轉後依然能保持二叉排序樹的特性且由不平衡變為平衡。可以用二叉排序樹的特性(”對於二叉排序樹,中序遍歷所得關鍵字序列自小至大有序”)證明之。
示意圖
算法分析
在平衡二叉排序樹上查找的時間復雜度為logn, 不會出現最差的情況。
代碼實現
1 //./a.out 45 12 53 3 37 100 24 61 90 78 2 //./a.out 45 12 53 100 61平衡二叉樹3 //測試 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <string.h> 7 8 #define DEBUG 9 #define TRUE 1 10 #define FALSE 0 11 #define LH +1 //左高 12 #define EH 0 //等高 13 #define RH -1 //右高 14 #define EQ(a,b) ((a)==(b)) 15 #define LT(a,b) ((a)< (b)) 16 #define LQ(a,b) ((a)<=(b)) 17 #define LCHILD 1 18 #define RCHILD 2 19 20 typedef int ElemType; 21 typedef int Boolean; 22 //平衡二叉樹采用二叉鏈表作為存儲結構 23 typedef struct BSTNode{ 24 ElemType data; 25 //結點的平衡因子 26 int bf; 27 //左,右孩子指針 28 struct BSTNode *lchild, *rchild; 29 }BSTNode, *BSTree; 30 31 /*右旋平衡處理算法 32 * 33 *對以*p為根的二叉排序樹作右旋處理,處理之後p指向新的樹根結點,即旋轉之前的左子樹的根結點。f始終指向*p的父親結點 34 *提示:建議結合圖9.13(a)看,此處*p, lc相當於圖中A、B結點。 35 */ 36 void R_Rotate(BSTree *p, BSTree f){ 37 //*p是其父親結點f的左孩子結點還是右孩子結點? 38 int flag = -1; 39 if(f && (f->lchild == (*p))){ 40 //*p是f的左孩子結點 41 flag = LCHILD; 42 } 43 if(f && (f->rchild == (*p))){ 44 //*p是f的右孩子結點 45 flag = RCHILD; 46 } 47 48 //lc指向*p的左子樹根結點 49 BSTNode *lc = (BSTNode*)(*p)->lchild; 50 //lc的右子樹掛接為*p的左子樹 51 (*p)->lchild = lc->rchild; 52 //p指向新的根結點 53 lc->rchild = *p; 54 *p = lc; 55 56 //更新父親結點f的孩子結點指針 57 if(f && (flag==LCHILD)){ 58 f->lchild = *p; 59 } 60 if(f && (flag==RCHILD)){ 61 f->rchild = *p; 62 } 63 } 64 65 /*左旋平衡處理算法 66 * 67 *提示:和右旋平衡算法是對稱的,建議結合圖9.13(c)看,此處*p,rc相當圖圖中的A、B結點。 68 */ 69 void L_Rotate(BSTree *p, BSTree f){ 70 int flag = -1; 71 if(f && (f->lchild == (*p))){ 72 flag = LCHILD; 73 } 74 if(f && (f->rchild == (*p))){ 75 flag = RCHILD; 76 } 77 78 BSTNode *rc = (BSTNode*)(*p)->rchild; 79 (*p)->rchild = rc->lchild; 80 rc->lchild = *p; 81 *p = rc; 82 83 if(f && (flag==LCHILD)){ 84 f->lchild = *p; 85 } 86 if(f && (flag==RCHILD)){ 87 f->rchild = *p; 88 } 89 } 90 91 //對指針T所指結點為根的二叉樹作左平衡選擇處理,本算法結束時,指針T指向新的根結點,f為*T的父親結點。 92 void LeftBalance(BSTree *T, BSTree f){ 93 //lc指向*T的左子樹根結點 94 BSTNode *lc = (BSTNode*)(*T)->lchild; 95 BSTNode *rd; 96 //檢查*T的左子樹的平衡度,並作相應平衡處理 97 switch(lc->bf){ 98 //新結點插在了*T的左孩子的左子樹上,要做單右旋處理 99 case LH: 100 lc->bf = (*T)->bf = EH; 101 R_Rotate(T, f); 102 break; 103 //新結點插在了*T的左孩子的右子樹上,要做雙旋處理 104 case RH: 105 //rd指向*T的左孩子的右子樹根 106 rd = lc->rchild; 107 switch(rd->bf){ 108 //修改*T及其左孩子的平衡因子。 109 //提示:建議結合圖9.13(b)看,此處*T, lc, rd相當於圖中A、B、C結點。 110 case LH: 111 (*T)->bf = RH; 112 lc->bf = EH; 113 break; 114 case EH: 115 (*T)->bf = EH; 116 lc->bf = EH; 117 break; 118 case RH: 119 (*T)->bf = EH; 120 lc->bf = LH; 121 break; 122 } 123 rd->bf = EH; 124 //對*T的左子樹lc做左旋平衡處理 125 L_Rotate(&lc, *T); 126 //對*T左右旋平衡處理 127 R_Rotate(T, f); 128 break; 129 default: 130 break; 131 } 132 return ; 133 } 134 135 //和左平衡算法是對稱的,此處不再贅述 136 void RightBalance(BSTree *T, BSTree f){ 137 BSTNode *rc = (BSTNode*)(*T)->rchild; 138 BSTNode *ld; 139 switch(rc->bf){ 140 case LH: 141 //提示:建議結合圖9.13(d)看,此處*T, rc, ld相當於圖中的A、B、C結點。 142 ld = rc->lchild; 143 switch(ld->bf){ 144 case LH: 145 (*T)->bf = EH; 146 rc->bf = RH; 147 break; 148 case EH: 149 (*T)->bf = EH; 150 rc->bf = EH; 151 break; 152 case RH: 153 (*T)->bf = LH; 154 rc->bf = EH; 155 break; 156 } 157 ld->bf = EH; 158 R_Rotate(&rc, *T); 159 L_Rotate(T, f); 160 break; 161 case RH: 162 rc->bf = (*T)->bf = EH; 163 L_Rotate(T, f); 164 break; 165 default: 166 break; 167 } 168 return ; 169 } 170 171 /*平衡二叉樹的插入算法 172 * 173 *若在平衡二叉排序樹中T不存在和e有相同關鍵字的結點,則插入一個數據元素為e 174 *的新結點點,並返回TRUE;否則返回FALSE。若因插入而使二叉排序樹失去平衡,則 175 *作平衡選擇處理,布爾變量taller反映T長高與否。 176 */ 177 int InsertAVL(BSTree *T,BSTree f, ElemType e, Boolean *taller){ 178 if(!(*T)){ 179 //插入新結點,樹"長高",置taller為TRUE,並返回TRUE 180 (*T) = (BSTree)malloc(sizeof(BSTNode)); 181 (*T)->data = e; 182 (*T)->bf = EH; 183 (*T)->lchild = (*T)->rchild = NULL; 184 *taller = TRUE; 185 return TRUE; 186 }else{ 187 if(EQ((*T)->data, e)){ 188 //樹中已經存在和e相同的結點,不再插入,並返回FALSE 189 *taller = FALSE; 190 return FALSE; 191 } 192 if(LT(e, (*T)->data)){ 193 //應該繼續在*T的左子樹上進行搜索 194 BSTree *p = malloc(sizeof(BSTree)); 195 *p = (BSTree)((*T)->lchild); 196 if(!InsertAVL(p, *T, e, taller)){ 197 //未插入 198 free(p); 199 return FALSE; 200 } 201 //已插入到*T的左子樹中, 更新*T的左子樹結點 202 (*T)->lchild = *p; 203 if(*taller){ 204 //左子樹"長高",檢查*T的平衡度 205 switch((*T)->bf){ 206 case LH: 207 //原本左子樹比右子樹高,現在左子樹上又長高了,需要作左平衡處理 208 LeftBalance(T, f); 209 (*T)->bf = EH; 210 *taller = FALSE; 211 break; 212 case EH: 213 //原本左子樹和右子樹等高,現在左子樹上又長高了,現在*T的左子樹比右子樹高 214 (*T)->bf = LH; 215 *taller = TRUE; 216 break; 217 case RH: 218 //原本左子樹和右子樹矮,現在左子樹上又長高了,現在*T的左子樹比右子樹等高 219 (*T)->bf = EH; 220 *taller = FALSE; 221 break; 222 } 223 } 224 free(p); 225 return TRUE; 226 }else{ 227 //應該繼續在*T的右子樹上進行搜索 228 BSTree *p2 = malloc(sizeof(BSTree)); 229 *p2= (BSTree)((*T)->rchild); 230 if(!InsertAVL(p2, *T, e, taller)){ 231 //未插入 232 free(p2); 233 return FALSE; 234 } 235 //已插入到*T的右子樹中, 更新*T的右子樹結點 236 (*T)->rchild = *p2; 237 if(*taller){ 238 //右子樹"長高",檢查*T的平衡度 239 switch((*T)->bf){ 240 case LH: 241 //原本左子樹比右子樹高,現在右子樹上長高了,現在*T的左子樹比右子樹等高 242 (*T)->bf = EH; 243 *taller = FALSE; 244 break; 245 case EH: 246 //原本左子樹和右子樹等高,現在右子樹上長高了,現在*T的左子樹比右子樹矮 247 (*T)->bf = RH; 248 *taller = TRUE; 249 break; 250 case RH: 251 //原本左子樹和右子樹矮,現在右子樹上長高了,需要作右平衡處理 252 RightBalance(T, f); 253 (*T)->bf = EH; 254 *taller = FALSE; 255 break; 256 } 257 } 258 free(p2); 259 return TRUE; 260 } 261 } 262 } 263 //二叉樹先根遍歷算法的函數聲明 264 int PreOrderTraverse(BSTree T); 265 //二叉樹中根遍歷算法的函數聲明 266 int InOrderTraverse(BSTree T); 267 //二叉樹後根遍歷算法的函數聲明 268 int PostOrderTraverse(BSTree T); 269 //分別以先、中、後根遍歷算法依次打印二叉樹中的結點元素的函數聲明 270 void print(BSTree T); 271 272 int main(int argc, char *argv[]) 273 { 274 if(argc < 2) 275 return FALSE; 276 int i = 0; 277 ElemType e; 278 Boolean taller; 279 BSTree Tree = NULL; 280 for(i=1; i<argc; i++){ 281 e = atoi(argv[i]); 282 printf("插入數據: %d\n", e); 283 InsertAVL(&Tree, NULL, e, &taller); 284 print(Tree); 285 286 } 287 return TRUE; 288 } 289 290 291 //分別以先、中、後根遍歷算法依次打印二叉樹中的結點元素的函數實現 292 void print(BSTree T){ 293 printf("先根遍歷:\t"); 294 PreOrderTraverse(T); 295 printf("\n"); 296 297 printf("中根遍歷:\t"); 298 InOrderTraverse(T); 299 printf("\n"); 300 301 printf("後根遍歷:\t"); 302 PostOrderTraverse(T); 303 printf("\n"); 304 } 305 306 307 308 //二叉樹先根遍歷算法的函數實現 309 int PreOrderTraverse(BSTree T){ 310 if(T){ 311 printf("[%-3d(%-2d)] ", ((BSTNode*)T)->data, ((BSTNode*)T)->bf); 312 PreOrderTraverse((BSTree)T->lchild); 313 PreOrderTraverse((BSTree)T->rchild); 314 } 315 return 0; 316 } 317 318 //二叉樹中根遍歷算法的函數實現 319 int InOrderTraverse(BSTree T){ 320 if(T){ 321 InOrderTraverse((BSTree)T->lchild); 322 printf("[%-3d(%-2d)] ", ((BSTNode*)T)->data, ((BSTNode*)T)->bf); 323 InOrderTraverse((BSTree)T->rchild); 324 } 325 return 0; 326 } 327 328 //二叉樹後根遍歷算法的函數實現 329 int PostOrderTraverse(BSTree T){ 330 if(T){ 331 PostOrderTraverse((BSTree)T->lchild); 332 PostOrderTraverse((BSTree)T->rchild); 333 printf("[%-3d(%-2d)] ", ((BSTNode*)T)->data, ((BSTNode*)T)->bf); 334 } 335 return 0; 336 }
運行
查找->動態查找表->平衡二叉樹