看完了第一篇博客,相信大家對於平衡二叉樹的插入調整以及刪除調整已經有了一定的了解,下面,我們開始介紹代碼部分。
首先,再次提一下使用的結構定義
1 typedef char KeyType; //關鍵字 2 typedef struct MyRcdType //記錄 3 { 4 KeyType key; 5 }RcdType,*RcdArr; 6 typedef enum MyBFStatus //為了方便平衡因子的賦值,這里進行枚舉 7 { //RH,EH,LH分別表示右子樹較高,左右子樹等高,左子樹較高 8 RH,EH,LH 9 }BFStatus; 10 typedef struct MyBBSTNode //樹結點類型定義 11 { 12 RcdType data; //數據成員 13 BFStatus bf; //平衡因子 14 struct MyBBSTNode *lchild,*rchild; //左右分支 15 }BBSTNode,*BBSTree;
1. 旋轉
旋轉是平衡二叉樹的基礎。所以我們首先介紹。先看具體代碼。RRotate的作用就是以*T為根結點的二叉樹向右旋轉,LRotate就是向左旋轉。
參數說明:*T為待旋轉子樹的根結點。
1 BBSTree RRotate(BBSTree *T) 2 { 3 BBSTree lchild; 4 lchild = (*T)->lchild; 5 (*T)->lchild = lchild->rchild; 6 lchild->rchild = (*T); 7 (*T) = lchild; 8 } 9 void LRotate(BBSTree *T) 10 { 11 BBSTree rchild; 12 rchild = (*T)->rchild; 13 (*T)->rchild = rchild->lchild; 14 rchild->lchild = *T; 15 *T = rchild; 16 }
2. 失衡調整
在前一篇博客我們已經知道,失衡的情況主要包括了兩種,左子樹過高與右子樹過高。我們按其對代碼進行划分,首先介紹左子樹過高的情況,我們稱為左平衡處理。
在這里,不得不再提一下,我們將左失衡,即做左子樹過高的情況分為了三種,插入新的結點時,可能出現的情況為LL與LR,刪除的時候可能出現的情況為LL,LR,LE。假如不明白為何LE只出現在刪除的時候出現,請查看上一篇博客的插入調整與刪除調整部分的內容。
函數先行條件:*T為根的樹為不平衡子樹。
參數說明:*T為不平衡子樹的根結點
函數大致說明:首先根據LL,LR,LE對不平衡樹進行划分,設定旋轉后的最終平衡因子,再對*T進行相應旋轉。例如,LL型的不平衡樹,最后為*T的左孩子作為根結點,首先設定為最終的值,*T的左孩子的平衡因子為EH,初始根的平衡因子也為EH。
void LeftBalance(BBSTree *T) { BBSTree lchild,rchild; lchild = (*T)->lchild; switch (lchild->bf) { case EH: (*T)->bf = lchild->bf = LH; lchild->bf = RH; RRotate(T); break; case LH: (*T)->bf = lchild->bf = EH; RRotate(T); break; case RH: rchild = lchild->rchild; switch (rchild->bf) { case LH: (*T)->bf = RH; lchild->bf = EH; break; case RH: (*T)->bf = EH; lchild->bf = LH; break; case EH: (*T)->bf = EH; lchild->bf = EH; } rchild->bf = EH; LRotate(&((*T)->lchild)); RRotate(T); break; } }
1 void RightBalance(BBSTree *T) 2 { 3 BBSTree rchild,lchild; 4 rchild = (*T)->rchild; 5 switch (rchild->bf) 6 { 7 case RH: 8 (*T)->bf = rchild->bf = EH; 9 LRotate(T); 10 break; 11 case EH: 12 (*T)->bf = RH; 13 rchild->bf = LH; 14 LRotate(T); 15 break; 16 case LH: 17 lchild = rchild->lchild; 18 switch (lchild->bf) 19 { 20 case LH: 21 rchild->bf = RH; (*T)->bf = EH; 22 break; 23 case RH: 24 rchild->bf = EH; (*T)->bf = LH; 25 break; 26 case EH: 27 rchild->bf = EH; (*T)->bf = EH; 28 break; 29 } 30 lchild->bf = EH; 31 RRotate(&((*T)->rchild)); 32 LRotate(T); 33 break; 34 } 35 }
3. 插入新的結點
說明:在插入新的結點的時候,我們使用一個taller的變量來記錄樹的高度是否變化。默認認為樹的高度是有增加的。我們在插入新的結點后,首先判斷樹的高度是否增加了,假如樹的高度沒有變化,不必進行如何操作。當樹的高度增加時,我們就考慮是否需要對樹的進行平衡調整。假如原本根的平衡因子為LH,而插入點又在左子樹上,並且子樹的高度變高了的時候,我們就要進行左平衡處理。相對的,假如原本根的平衡因子為RH,而插入點又在右子樹上,並且子樹的高度變高了的時候,我們就要進行右平衡處理。而我們又知道,只需要對最小失衡樹進行平衡調整,所以調整后要將taller置為FALSE
參數說明:*T,待插入的平衡二叉樹
e,帶插入的新的結點的值
taller,*T的子樹的高度是否變高的標志。
Status InsertAVL(BBSTree *T,RcdType e,Status *taller) { if(!(*T)) //新建一個節點 return CreatBBSTNode(T,e); else if(e.key == (*T)->data.key) { *taller = FALSE; return TRUE; } if(e.key < (*T)->data.key) //插入到左子樹 { Status sign = InsertAVL(&(*T)->lchild,e,taller); if(FALSE == sign || OVERFLOW == sign) return FALSE; if(TRUE == *taller) { switch ((*T)->bf) { case LH: LeftBalance(T); *taller = FALSE; break; case EH: (*T)->bf = LH; *taller = TRUE; break; case RH: (*T)->bf = EH; *taller = FALSE; break; } } } else //插入到了右子樹 { Status sign = InsertAVL(&(*T)->rchild,e,taller); if(FALSE == sign || OVERFLOW == sign) return FALSE; if(TRUE == *taller) { switch ((*T)->bf) { case LH: (*T)->bf = EH; *taller = FALSE; break; case EH: (*T)->bf = RH; *taller = TRUE; break; case RH: RightBalance(T); *taller = FALSE; break; } } } return TRUE; }
4. 刪除
說明:平衡二叉樹也是一棵二叉查找樹,所以其刪除操作與二叉查找樹是一致的。只是我們需要進行平衡處理。我們使用bfChild記錄待刪除結點的的子樹的根結點的原平衡因子。新的平衡因子由子樹的根結點的bf成員進行記錄。當待刪除的結點處於當前節點的左分支上時,刪除結點后,我們調用DelLeftCase設定樹的平衡因子以及對樹進行調整。當待刪除的結點處於當前節點的右分支上時,刪除結點后,我們調用DelRightCase設定樹的平衡因子以及對樹進行調整。而我們需要對結點的平衡因子進行重新設定,只有在子樹的高度有所降低時進行。而子樹的高度降低,對應着Del***Case函數中的子樹是否變為NULL或者子樹的平衡因子從LH或者RH變為EH。
參數說明:*T為待進行調整的子樹的根結點
bfChild為*T的左孩子在刪除結點前的平衡因子
1 //參數說明:*T為待進行調整的子樹的根結點 2 //bfChild為*T的右孩子在刪除結點前的平衡因子 3 void DelLeftCase(BBSTree *T,int bfChild) 4 { 5 //當bf為-1或1變為0,或者孩子為空時說明子樹高降低 6 if((!(*T)->lchild) || (EH != bfChild && EH == (*T)->lchild->bf)) 7 { 8 switch ((*T)->bf)//左子樹樹高降低 9 { 10 case EH: 11 (*T)->bf = RH; 12 break; 13 case LH: 14 (*T)->bf = EH; 15 break; 16 case RH: //原本右子樹比較高 17 RightBalance(T); 18 break; 19 } 20 } 21 } 22 23 void DelRightCase(BBSTree *T,int bfChild) 24 { 25 //當bf為LH或RH變為EH,或者孩子為空時說明子樹高降低 26 if((!(*T)->rchild) || (EH != bfChild && EH == (*T)->rchild->bf)) 27 { 28 switch ((*T)->bf) 29 { 30 case EH: 31 (*T)->bf = LH; 32 break; 33 case RH: 34 (*T)->bf = EH; 35 break; 36 case LH: //原本左子樹比較高 37 LeftBalance(T); 38 break; 39 } 40 } 41 } 42 BBSTree DeleteNode(BBSTree *T,KeyType key) 43 { 44 int bfChild; 45 if(*T) 46 { 47 if((*T)->data.key > key) 48 { 49 bfChild = (*T)->lchild->bf; 50 (*T)->lchild = DeleteNode(&(*T)->lchild,key); 51 DelLeftCase(T,bfChild); 52 } 53 else if((*T)->data.key < key) 54 { 55 bfChild = (*T)->rchild->bf; 56 (*T)->rchild = DeleteNode(&(*T)->rchild,key); 57 DelRightCase(T,bfChild); 58 } 59 else//當前節點就是要刪除的節點 60 { 61 if((*T)->lchild) //*T不是葉子結點並且具有直接前驅 62 { 63 BBSTree farRight = GofarRight((*T)->lchild); 64 (*T)->data = farRight->data; 65 //可以確定,刪除的節點為當前節點的左子樹的某一個節點 66 (*T)->lchild = DeleteNode(&(*T)->lchild,farRight->data.key); 67 DelLeftCase(T,bfChild); 68 } 69 else if((*T)->rchild) //*T不是葉子結點並且具有直接后驅 70 { 71 BBSTree farLeft = GofarLeft((*T)->rchild); 72 (*T)->data = farLeft->data; 73 (*T)->rchild = DeleteNode(&(*T)->rchild,farLeft->data.key); 74 DelRightCase(T,bfChild); 75 } 76 else //*T是葉子結點 77 { 78 free(*T); 79 *T = NULL; 80 } 81 } 82 } 83 return (*T);//包含了返回NULL與正常的當前節點 84 }
假如您看了圖解篇以及代碼篇,或許對AVL有了一定的了解。不過還是建議將所有的代碼自己實現一次。下面附上完整測試代碼的下載鏈接:http://yunpan.cn/cwUVwIYbPwsC8 訪問密碼 4302。
測試代碼大概解釋:使用#表示空結點。代碼先自動生成一棵平衡二叉樹,首先刪除一個結點,用戶輸入Y就會再次刪除平衡二叉樹的一個結點。每次刪除完了會將樹進行輸出。輸入的格式為A【B,C】的格式,A代表根結點,B為A的左孩子,C為A的右孩子。輸入其他字符可以查看最終結果。
PS:假如代碼無法編譯,請檢查隨機函數在你的編譯器是否可用。
歡迎各位朋友批評改正,謝謝。
