一、定義概覽
AVL樹是最先發明的自平衡二叉查找樹。在AVL樹中任何節點的兩個子樹的高度最大差別為一,所以它也被稱為高度平衡樹。查找、插入和刪除在平均和最壞情況下都是O(log n)。增加和刪除可能需要通過一次或多次樹旋轉來重新平衡這個樹。
節點的平衡因子是它的左子樹的高度減去它的右子樹的高度(有時相反)。帶有平衡因子1、0或 -1的節點被認為是平衡的。帶有平衡因子 -2或2的節點被認為是不平衡的,並需要重新平衡這個樹。平衡因子可以直接存儲在每個節點中,或從可能存儲在節點中的子樹高度計算出來。
一般我們所看見的都是排序平衡二叉樹
二、一般性質
AVL樹具有以下性質:它是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,並且左右兩個子樹都是一棵平衡二叉樹。在平衡二叉搜索樹中,我們可以看到,其高度一般都良好地維持在O(log2n),其各操作的時間復雜度(O(log2n))同時也由此而決定,大大降低了操作的時間復雜度。另外,最小二叉平衡樹的節點的公式如下 F(n)=F(n-1)+F(n-2)+1 這個類似於一個遞歸的數列。
三、一般操作
AVL樹的基本操作一般涉及運作同在不平衡的二叉查找樹所運作的同樣的算法。但是要進行預先或隨后做一次或多次所謂的"AVL旋轉"。
1.插入
向AVL樹插入可以通過如同它是未平衡的二叉查找樹一樣把給定的值插入樹中,接着自底向上向根節點折回,於在插入期間成為不平衡的所有節點上進行旋轉來完成。因為折回到根節點的路途上最多有1.5乘log n個節點,而每次AVL旋轉都耗費恆定的時間,插入處理在整體上耗費O(log n) 時間
2.刪除
從AVL樹中刪除可以通過把要刪除的節點向下旋轉成一個葉子節點,接着直接剪除這個葉子節點來完成。因為在旋轉成葉子節點期間最多有log n個節點被旋轉,而每次AVL旋轉耗費恆定的時間,刪除處理在整體上耗費O(log n) 時間。
3.查找
可以像普通二叉查找樹一樣的進行,所以耗費O(log n)時間,因為AVL樹總是保持平衡的。不需要特殊的准備,樹的結構不會由於查找而改變。(這是與伸展樹查找相對立的,它會因為查找而變更樹結構。)
四、左右旋操作
1.單向右旋平衡處理LL:由於在*a的左子樹根節點的左子樹上插入節點,*a的平衡因子由1增至2,致使以*a為根的子樹失去平衡,則需進行一次右旋轉操作;
2.單向左旋平衡處理RR:由於在*a的右子樹根節點的右子樹上插入節點,*a的平衡因子由-1變為-2,致使以*a為根的子樹失去平衡,則需進行一次左旋轉操作
3.雙向旋轉(先左后右)平衡處理LR:由於在*a的左子樹根節點的右子樹上插入節點,*a的平衡因子由1增至2,致使以*a為根的子樹失去平衡,則需進行兩次旋轉(先左旋后右旋)操作
4.雙向旋轉(先右后左)平衡處理RL:由於在*a的右子樹根節點的左子樹上插入節點,*a的平衡因子由-1變為-2,致使以*a為根的子樹失去平衡,則需進行兩次旋轉(先右旋后左旋)操作。
五、相關代碼實現(代碼參考網絡資料,有修改,未測試)
1.基本結構體及變量
#define LH +1 // 左高 #define EH 0 // 等高 #define RH -1 // 右高 // 平衡二叉樹的類型 struct AVLNode { int data; int bf; //bf結點的平衡因子,只能夠取0,-1,1,它是左子樹的深度減去右子樹的深度得到的 struct AVLNode *lchild,*rchild; // 左、右孩子指針 };
2.右旋操作:
void R_Rotate(AVLNode *&p) { AVLNode *lc=p->lchild; // lc指向p的左子樹根結點 p->lchild=lc->rchild; // lc的右子樹掛接為p的左子樹 lc->rchild=p; p=lc; // p指向新的根結點 }
3.左旋操作:
void L_Rotate(AVLNode *&p) { AVLNode *rc=p->rchild; // rc指向p的右子樹根結點 p->rchild=rc->lchild; // rc的左子樹掛接為p的右子樹 rc->lchild=p; p=rc; // p指向新的根結點 }
4.對樹進行左平衡操作:
void LeftBalance(AVLNode *&T) { AVLNode *lc,*rd; lc=T->lchild; // lc指向T的左子樹根結點 switch(lc->bf) { case LH: // 1 新結點插入在*T的左孩子的左子樹上,要作單右旋處理 T->bf=lc->bf=EH; R_Rotate(T); break; case RH: // -1 新結點插入在*T的左孩子的右子樹上,要作雙旋處理 rd=lc->rchild; // rd指向*T的左孩子的右子樹根 switch(rd->bf) { // 根據旋轉后的效果去修改T及其左孩子的平衡因子 以下右旋轉類似 case LH: T->bf=RH; lc->bf=EH; break; case EH: T->bf=lc->bf=EH; break; case RH: T->bf=EH; lc->bf=LH; } rd->bf=EH; L_Rotate(T->lchild); R_Rotate(T); } }
5.對樹進行右平衡操作:
void RightBalance(AVLNode *&T) { AVLNode *rc,*rd; rc=T->rchild; switch(rc->bf) { case RH: T->bf=rc->bf=EH; L_Rotate(T); break; case LH: rd=rc->lchild; switch(rd->bf) { case RH: T->bf=LH; rc->bf=EH; break; case EH: T->bf=rc->bf=EH; break; case LH: T->bf=EH; rc->bf=RH; } rd->bf=EH; R_Rotate(T->rchild); L_Rotate(T); } }
6.插入操作:
int InsertAVL(AVLNode *&T,int data,int *taller) { if(!T) //此時為初始情況 或已找到適當位置插入新數據 { T=(AVLNode *)malloc(sizeof(AVLNode)); T->data=data; T->lchild=T->rchild=NULL; T->bf=EH; *taller=1; } else { if(data == T->data) { *taller=0; return 0; } if(data < T->data) { if(!InsertAVL(T->lchild,data,taller)) return 0; if(*taller) switch(T->bf) { case LH: //插入前做左子樹比右子樹高,插入后,左子樹已經長高, 排序樹失去平衡 LeftBalance(T); //對排序樹進行右平衡操作 *taller=0; break; case EH: T->bf=LH; *taller=1; //這里標識有所長高 實際上此時父節點或者祖父結點的平衡因子的絕對值已經大於1 break; case RH: //插入前右子樹比左子樹高,插入后左右子樹深度相等 T->bf=EH; *taller=0; //標志沒長高 } } else { if(!InsertAVL(T->rchild,data,taller)) return 0; if(*taller) switch(T->bf) { //插入前左子樹比右子樹高,插入后左右子樹深度相等 case LH: T->bf=EH; //標志沒長高 *taller=0; break; case EH: //這里標識有所長高 實際上此時父節點或者祖父節點的平衡因子的絕對值已經大於1 T->bf=RH; *taller=1; break; case RH: //插插入前做右子樹比左子樹高,插入后,右子樹已經長高, 排序樹失去平衡 RightBalance(T); //對排序樹進行右平衡操作 *taller=0; } } } return 1; }
7.刪除操作。。待續