在上一篇文章查找-之二叉排序樹(查找、插入、刪除)引出的問題是:
二叉排序樹的存在的不足是插入新結點導致樹不平衡,不平衡樹使得查找性能下降
解決方法
構建平衡的二叉樹
AVL樹、紅黑樹
AVL樹:帶有嚴格平衡條件的二叉查找樹,用平衡因子差值判斷樹是否平衡並通過旋轉實現平衡,左右子樹的高度不超過1
適用於:插入和刪除次數較少(插入刪除操作使得樹失去平衡,爲維持嚴格平衡旋轉操作,使得性能下降),查找次數較多的情況
Windows NT內核中廣泛存在
算法複雜度:時間複雜度,查找O(logn), 插入和刪除 O(logn)
存在的問題:
AVL樹的每個結點只能存放一個元素、並且每個結點只有兩個子結點,查找時需要多次磁盤IO操作,每次查詢從磁盤中的一頁數據拷貝到內存,其中樹的每一層結點存放在一頁中,不同層數據存放在不同頁,爲此查找數據需要多次操作磁盤IO
紅黑樹:弱平衡二叉查找樹,通過對任何一條根到葉子路徑各個結點着色來限制,確保沒有一條路徑會比其它路徑長出兩倍
適用於:插入、刪除次數較多,查找次數較多的情況
應用:
linux進程調度用紅黑樹管理進程控制塊
Java Treemap、TreeSet
C++ STL
Nginx 中的定時器管理
算法複雜度:
查找、刪除、插入操作的時間複雜度:O(logn), O(log2 (n))
統計性能好於AVL樹
AVL樹:
1)左子樹高於右子樹,則旋轉因子BF爲正,該結點對應的整棵樹右旋
2)右子樹高於左子樹,則旋轉因子BF爲負,該結點對應的整棵樹左旋
3)符號不統一,雙旋
C僞代碼:
//二叉樹的二叉鏈表結點結構定義
typedef struct BiTNode
{
int data; //結點數據
int bf; //結點平衡因子
struct BiTNode *lchild,*rchild;//左右孩子指針
} BiTNode,*BiTree;
//對以P爲根的二叉排序樹做右旋處理
void R_Rotate(BiTree *P)
{
BiTree L;
L=(*P)->lchild;
(*P)->lchild=L->rchild;
L->rchild=(*P);
*P=L;//P指向新的根結點
}
//對以P爲根的二叉排序樹做左旋處理
void L_Rotate(BiTree *P)
{
BiTree R;
R=(*P)->rchild;
(*P)->rchild=R->lchild;
R->lchild=(*P);
*P=R;
}
//左平衡旋轉處理的函數代碼
#define LH +1 //左高
#define EH 0 // 等高
#define RH -1 // 右高
//對指針T所指結點爲根的二叉樹作左平衡旋轉處理,左子樹的高度大於右子樹的高度
void LeftBalance(BiTree *T)
{
BiTree L,Lr;
L=(*T)->lchild;//L指向左子樹的根結點
switch(L->bf)
{
//檢查左子樹的平衡度,並做相應的平衡處理
case LH://+1 說明L->bf和根結點的符號相同, (新結點插在T的左孩子的左子樹上)----右旋處理
(*T)->bf=L->bf=EH;
R_Rotate(T);
break;
case RH:// -1 說明L->bf -1和根結點的符號相反 , (新結點插入在T的左孩子的右子樹上) -----雙旋處理
Lr=L->rchild;//Lr指向T的左孩子的右子樹根
switch(Lr->bf)//T的左孩子的右子樹根做判斷---修改T及T的左孩子L的平衡因子 ??
{
case LH: //左高 +1
(*T)->bf=RH;//-1
L->bf=EH;//0
break;
case EH://等高 0
(*T)->bf=L->bf=EH;//0
break;
case RH: //右高 -1
(*T)->bf=EH;//0
L->bf=LH;//+1
break;
}
Lr->bf=EH;//0
L_Rotate(&(*T)->lchild);//T的左孩子左旋
R_Rotate(T);//T右旋
}
}
// 若在平衡二叉排序樹T中不存在和e有相同關鍵字的結點,則插入一個
//因插入數據而使得二叉排序樹失去平衡,則做平衡旋轉處理
//taller 反映樹T長高與否
Status InsertAVL(BiTree *T,int e,Status *taller)
{
if(!*T)//如果樹空
{
*T=(BiTree)malloc(sizeof(BiTNode));
(*T)->data=e;
(*T)->lchild=(*T)->rchild=NULL;
(*T)->bf=EH;
*taller=True;
}
else
{
if(e==(*T)->data)//樹中已存在和e有相同關鍵字的結點--則不再插入
{
*taller=False;
return False;
}
if(e<(*T)->data)//待插入的數小於根節點 ---在T的左子樹搜索
{
//繼續在T的左子樹中進行搜索
if(!InsertAVL(&(*T)->lchild,e,taller))//若在左子樹中找到該插入的結點,則返回false
return False;
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)) //若在右子樹中找到該插入的結點,則返回false ,未插入
{
return False;
}
if(*taller)//插入T的右子樹且右子樹長高
{
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;
}
構建平衡二叉樹的示例
//構建平衡二叉樹
int i;
int a[10]={3,2,1,4,5,6,7,10,9,8};
BiTree T=NULL;
Status taller;
for(i=0;i<10;i++)
{
InsertAVL(&T,a[i],&taller);
}
參考
《大話數據結構》