資料結構——AVL樹
阿新 • • 發佈:2018-12-25
AVL樹是最早提出的自平衡二叉樹,在AVL樹中任何節點的兩個子樹的高度最大差別為一,即|HL-HR|<=1,所以它也被稱為高度平衡樹。AVL樹得名於它的發明者G.M. Adelson-Velsky和E.M. Landis,他們在1962年的論文《An algorithm for the organization of information》中發表了它。
AVL樹中查詢、插入和刪除在平均和最壞情況下都是O(log n),增加和刪除可能需要通過一次或多次樹旋轉來重新平衡這個樹。確定並刪除識別符號為x的元素、及確定並刪除第k個最小元素都可以在O(log
n)時間內完成。
有四種插入方式會打破樹的平衡:
(1)LL:插入一個新節點到根節點的左子樹(Left)的左子樹(Left),導致根節點的平衡因子由1變為2
(2)RR:插入一個新節點到根節點的右子樹(Right)的右子樹(Right),導致根節點的平衡因子由-1變為-2
(3)LR:插入一個新節點到根節點的左子樹(Left)的右子樹(Right),導致根節點的平衡因子由1變為2
(4)RL:插入一個新節點到根節點的右子樹(Right)的左子樹(Left),導致根節點的平衡因子由-1變為-2
針對四種種情況可能導致的不平衡,可以通過旋轉使之變平衡。有兩種基本的旋轉:
(1)左旋轉:將根節點旋轉到(根節點的)右孩子的左孩子位置
(2)右旋轉:將根節點旋轉到(根節點的)左孩子的右孩子位置
注意:旋轉需要保證的是該數的中序遍歷節點的順序不變!基本的資料結構:
typedef struct BSTNode
{
int data;
int bf; //結點的平衡因子
struct BSTNode *lchild,*rchild;//左、右孩子指標
}BSTNode,*BSTree;
右旋轉:
程式碼如下:
左旋轉://對以*p為根的二叉排序樹作右旋處理,LL型平衡旋轉法 void R_Rotate(BSTree &p) { BSTree lc; lc = p->lchild; //lc指向的*p左子樹根結點 p->lchild = lc->rchild; //lc的右子樹掛接為*p的左子樹 lc->rchild = p; p = lc; //p指向新的結點 }
程式碼如下:
//對以*p為根的二叉排序樹作左旋處理,RR型平衡旋轉法
void L_Rotate(BSTree &p)
{
BSTree rc;
rc = p->rchild; //rc指向的*p右子樹根結點
p->rchild = rc->lchild; //rc的左子樹掛接為*p的右子樹
rc->lchild = p;
p = rc; //p指向新的結點
}
先左旋、後右旋:
程式碼如下:
//對以指標T所指結點為根的二叉樹作左平衡旋轉處理,LR型平衡旋轉法
void LeftBalance(BSTree &T)
{
BSTree lc,rd;
lc = T->lchild; //lc指向*T的左子樹根結點
switch(lc->bf) //檢查*T的左子樹的平衡度,並作相應平衡處理
{
case LH: //新結點插入在*T的左孩子的左子樹上,要作單右旋處理
T->bf = lc->bf = EH;
R_Rotate(T); break;
case RH: //新結點插入在*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; break;
}
rd->bf = EH;
L_Rotate(T->lchild); //對*T的左子樹作左旋平衡處理
R_Rotate(T); //對*T作右旋平衡處理
}
}
先右旋轉後左旋轉同上。插入操作:就是在不同情況下采用旋轉方式調整AVL樹的平衡
程式碼如下:
//插入結點e,若T中不存在和e相同關鍵字的結點,則插入一個數據元素為e的新結點,並返回1,否則返回0
bool InsertAVL(BSTree &T,int e,bool &taller)
{
if(!T)//插入新結點,樹"長高",置taller為true
{
T = (BSTree)malloc(sizeof(BSTNode));
T->data = e;
T->lchild = T->rchild =NULL;
T->bf = EH; taller = true;
}
else
{
if(EQ(e,T->data)) //樹中已存在和有相同關鍵字的結點則不再插入
{
taller = false;
printf("The node have already exist!\n");
return 0;
}
if(LT(e,T->data)) //應繼續在*T的左子樹中進行搜尋
{
if(!InsertAVL(T->lchild,e,taller))
return 0;//未插入
if(taller) //已插入到*T的左子樹中且左子樹"長高"
{
switch(T->bf) //檢查*T的平衡度
{
case LH: //原本左子樹比右子樹高,需要作左平衡處理
LeftBalance(T);
taller = false; break;
case EH: //原本左子樹、右子等高,現因左子樹增高而使樹增高
T->bf = LH;
taller = true; break;
case RH: //原本右子樹比左子樹高,現左、右子樹等高
T->bf = EH;
taller = false; break;
}
}
}
else //應繼續在*T的右子樹中進行搜尋
{
if(!InsertAVL(T->rchild,e,taller))
return 0;//未插入
if(taller) //已插入到*T的右子樹中且右子樹"長高"
{
switch(T->bf) //檢查*T的平衡度
{
case LH: //原本左子樹比右子樹高,現左、右子樹等高
T->bf = EH; taller = false; break;
case EH: //原本左子樹、右子等高,現因右子樹增高而使樹增高
T->bf = RH; taller = true; break;
case RH: //原本右子樹比左子樹高,需要作右平衡處理
RightBalance(T); taller = false; break;
}
}
}
}
return 1;
}//InsertAVL
刪除操作:首先定位要刪除的節點,然後用該節點的右孩子的最左孩子替換該節點,並重新調整以該節點為根的子樹為AVL樹,具體調整方法跟插入資料類似。
程式碼如下:
刪除節點時左平衡旋轉操作處理:
//刪除結點時左平衡旋轉處理
void LeftBalance_div(BSTree &p,int &shorter)
{
BSTree p1,p2;
if(p->bf==1) //p結點的左子樹高,刪除結點後p的bf減1,樹變矮
{
p->bf=0; shorter=1;
}
else if(p->bf==0)//p結點左、右子樹等高,刪除結點後p的bf減1,樹高不變
{
p->bf=-1; shorter=0;
}
else //p結點的右子樹高
{
p1=p->rchild;//p1指向p的右子樹
if(p1->bf==0)//p1結點左、右子樹等高,刪除結點後p的bf為-2,進行左旋處理,樹高不變
{
L_Rotate(p);
p1->bf=1;
p->bf=-1;
shorter=0;
}
else if(p1->bf==-1)//p1的右子樹高,左旋處理後,樹變矮
{
L_Rotate(p);
p1->bf=p->bf=0; shorter=1;
}
else //p1的左子樹高,進行雙旋處理(先右旋後左旋),樹變矮
{
p2=p1->lchild;
p1->lchild=p2->rchild; p2->rchild=p1; p->rchild=p2->lchild; p2->lchild=p;
if(p2->bf==0)
{
p->bf=0; p1->bf=0;
}
else if(p2->bf==-1)
{
p->bf=1;p1->bf=0;
}
else
{
p->bf=0;
p1->bf=-1;
}
p2->bf=0;
p=p2;
shorter=1;
}
}
}
刪除節點時右旋轉操作處理:
//刪除結點時右平衡旋轉處理
void RightBalance_div(BSTree &p,int &shorter)
{
BSTree p1,p2;
if(p->bf==-1)
{
p->bf=0; shorter=1;
}
else if(p->bf==0)
{
p->bf=1; shorter=0;
}
else
{
p1=p->lchild;
if(p1->bf==0)
{
R_Rotate(p);
p1->bf=-1; p->bf=1; shorter=0;
}
else if(p1->bf==1)
{
R_Rotate(p);
p1->bf=p->bf=0; shorter=1;
}
else
{
p2=p1->rchild;
p1->rchild=p2->lchild; p2->lchild=p1; p->lchild=p2->rchild; p2->rchild=p;
if(p2->bf==0)
{
p->bf=0;
p1->bf=0;
}
else if(p2->bf==1)
{
p->bf=-1;
p1->bf=0;
}
else
{
p->bf=0; p1->bf=1;
}
p2->bf=0; p=p2; shorter=1;
}
}
}