新聞中心
平衡二叉樹概念
平衡二叉排序樹(Balanced Binary Tree),因由前蘇聯(lián)數(shù)學(xué)家Adelson-Velskii 和
Landis于1962年首先提出的,所以又稱為AVL樹。
網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)建站!專注于網(wǎng)頁設(shè)計、網(wǎng)站建設(shè)、微信開發(fā)、小程序開發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了福建免費建站歡迎大家使用!
平衡二叉樹是一種特殊的二叉排序樹,理解平衡二叉樹首先要理解什么是二叉排序樹。
如果已經(jīng)了解二叉排序樹可以直接看下面平衡二叉樹內(nèi)容。
二叉排序樹(Binary Sort Tree)
所謂二叉排序樹(BST)即:
(1)若該樹的左子樹不為空,那么左子樹所有結(jié)點的值均小于其根結(jié)點的值。
(2)若該樹的右子樹不為空,那么右子樹所有結(jié)點的值均大于其根結(jié)點的值。
(3)該樹的左右子樹也均為二叉排序樹。
依此定義,我們可以通過比較根結(jié)點的值一層層地定位到所要查找的值。
例:如下圖是一棵二叉排序樹
比如我們要查找7,那么先從根結(jié)點開始比較,8>7查找左子樹 ----> 3<7查找右子樹 ----> 6>7查找右子樹 ----> 最后7=7,找到了7。
這種查找算法與折半查找相似,也是逐步縮小搜索范圍。
若中序遍歷上圖,則可以得到一個按數(shù)值大小排序的遞增序列:1,3,4,6,7,8,10,13,15。
二叉排序樹的儲存結(jié)構(gòu)
typedef struct BSTNode
{
int data=0;//數(shù)據(jù)項
struct BSTNode *lchild=NULL,*rchild=NULL;//左右子樹
}BSTNode,*BSTree.
二叉排序樹的查找算法(遞歸查找)
BSTree T 為二叉樹根節(jié)點 ,int e 為查找的關(guān)鍵字。時間復(fù)制度為O(log2 n)。
int searchTree(BSTree T,int e)//在二叉樹中查找給定關(guān)鍵字(函數(shù)返回值為成功1,失敗0)
{
if (T==NULL)//無法查找到給定關(guān)鍵字
{
return 0;
}else if (e==T->data)//查找到關(guān)鍵字
{
return 1;
}else if (edata)//小于根結(jié)點,向左子樹查找
{
return searchTree(T->lchild,e);
}else if(e>T->data)//大于根結(jié)點,向右子樹查找
{
return searchTree(T->rchild,e);
}
}
二叉排序樹的創(chuàng)建及插入(遞歸)
二叉排序樹的插入算法基本過程也是查找,時間復(fù)制度也為O(log2 n)。
void InsertBST(BSTree &T,int e)//插入節(jié)點,根據(jù)節(jié)點值的大小插入
{//當(dāng)二叉排序樹中不存在關(guān)鍵字等于e的結(jié)點時,查找結(jié)束,插入結(jié)點
if (!T)//查找到插入位置
{
BSTNode *S; //生成新結(jié)點
S=new BSTNode;
S->data=e;//給新結(jié)點賦值
S->lchild=S->rchild=NULL;//將新結(jié)點作為葉子結(jié)點
T=S;//給查找到的插入位置賦值
}
else if (edata)
{
InsertBST(T->lchild,e);//向左查找插入
}else if (e>T->data)
{
InsertBST(T->rchild,e);//向右查找插入
}
}
void CreatBST(BSTree &T)//創(chuàng)建二叉樹
{
T=NULL;
int e,n,i;
scanf("%d",&n);
for (i=0;i
平衡二叉樹
介紹完二叉排序樹,我們就來看看平衡二叉樹。
平衡二叉樹就是在二叉排序樹上建立的,可以說是具有以下兩個特征的二叉排序樹:
(1)左子樹和右子樹的深度之差的絕對值不超過1。
(2)左子樹和右子樹也是平衡二叉樹。
平衡因子
為了方便記錄和計算左右子樹深度之差,我們引入一個概念叫平衡因子。
平衡因子就是該結(jié)點左右子樹深度之差,由平衡二叉樹的定義我們可以知道平衡二叉樹上的平衡因子只可能是-1,0,1 。
二叉排序樹的儲存結(jié)構(gòu)
typedef struct AVLNode
{
int data=0;//結(jié)點值
int depth=0;//深度,方便通過計算左右子樹深度之差得到該結(jié)點的平衡因子
struct AVLNode *father=NULL;//父結(jié)點
struct AVLNode *lchild=NULL,*rchild=NULL;//左右結(jié)點
} AVLNode,*AVLTree; //結(jié)點結(jié)構(gòu)體
計算平衡因子代碼實現(xiàn):
int count_depth(AVLTree &T)//計算各結(jié)點的深度
{
if(T==NULL)
{
return 0;
}
else
{
int l=count_depth(T->lchild);//左子樹深度
int r=count_depth(T->rchild);//右子樹深度
return T->depth=max(l,r)+1;//更新深度
}
}
int get_balance(AVLTree T)//讀取深度
{
if(T)
{
return T->depth;
}
else
{
return 0;
}
}
int count_balance(AVLTree T)//計算平衡因子
{
if(!T)
{
return 0;
}
else
{
return get_balance(T->lchild)-get_balance(T->rchild);//平衡因子等于左右子樹的深度之差
}
}
平衡二叉樹的創(chuàng)建及調(diào)整方法
如何創(chuàng)建一棵平衡二叉樹呢?
簡單的來說就是:
(1)先按照我們創(chuàng)建二叉排序樹的方法,找到結(jié)點插入位置,將結(jié)點插入二叉樹中。
(2)然后我們來計算該插入結(jié)點父結(jié)點的平衡因子, 如果平衡因子絕對值大于1,代表該子樹是不平衡的,那么對該子樹進(jìn)行調(diào)整,將其調(diào)整為平衡二叉樹, 然后一層一層往上計算平衡因子和進(jìn)行調(diào)整,直到根節(jié)點(根結(jié)點平衡因子絕對值大于1也要調(diào)整),最終把整棵樹調(diào)整為平衡二叉樹。
(3)第三步就重復(fù)上面操作,把一個一個結(jié)點插入二叉樹中并進(jìn)行調(diào)整,最終插入完所有結(jié)點并創(chuàng)建完成平衡二叉樹。
平衡二叉樹的調(diào)整
平衡二叉樹的調(diào)整有4種調(diào)整情況。
這里有一篇寫得比較好的文章可以參考一下:平衡二叉樹(AVL)圖解與實現(xiàn)-zthgreat
LL型
(a)
圖a中我們插入“16” 后計算平衡因子發(fā)現(xiàn)結(jié)點“31”的平衡因子為2,該樹為非平衡樹,需要進(jìn)行調(diào)整,我們以結(jié)點“25”為旋轉(zhuǎn)中心
,將其父結(jié)點
按順時針旋轉(zhuǎn)
為其右子樹
,至此該樹調(diào)整完畢。
注意
:25的左結(jié)點16不進(jìn)行旋轉(zhuǎn)。
(b)
圖b中我們插入“9”后計算平衡因子發(fā)現(xiàn)結(jié)點“31”的平衡因子為2,該樹為非平衡樹,需要進(jìn)行調(diào)整,我們以結(jié)點“25”為旋轉(zhuǎn)中心
,將其父結(jié)點“31”
按順時針旋轉(zhuǎn)
為其右子樹
,再把旋轉(zhuǎn)中心“25”的右子樹
調(diào)整為31
的左子樹,至此該樹調(diào)整完畢。
?? 我們總結(jié)出以下規(guī)律
(參考下圖):將B
結(jié)點作為根結(jié)點,A
結(jié)點旋轉(zhuǎn)為B
結(jié)點的右子樹,再把B
結(jié)點本來的右子樹連接為A
結(jié)點的左子樹,該樹調(diào)整完成。
代碼實現(xiàn):
AVLTree LL_rotate(AVLTree T)
{
AVLTree parent=NULL,son;//son結(jié)點即為旋轉(zhuǎn)中心
parent=T->father;//獲取失衡結(jié)點的父節(jié)點
son=T->lchild;//獲取失衡結(jié)點的左孩子
if (son->rchild!=NULL)//設(shè)置son結(jié)點右孩子的父指針
son->rchild->father=T;
T->lchild=son->rchild;//失衡結(jié)點的左孩子變更為son的右孩子
//T的子結(jié)點更新完畢
count_depth(T);//更新失衡結(jié)點的深度信息
son->rchild=T;//失衡結(jié)點變成son的右孩子
son->father=parent;//設(shè)置son的父結(jié)點為原失衡結(jié)點的父結(jié)點,連接整顆樹
//如果失衡結(jié)點不是根結(jié)點,則更新父節(jié)點
if (parent!=NULL)
{
//如果父節(jié)點的左孩子是失衡結(jié)點,指向現(xiàn)在更新后的新孩子son
if (parent->lchild==T)
parent->lchild=son;
else //父節(jié)點的右孩子是失衡結(jié)點
parent->rchild=son;
}
T->father=son;//設(shè)置失衡結(jié)點的父親
count_depth(son);//更新son結(jié)點的高度信息
return son;
}
RR型
(a)
圖a中我們插入“69” 后計算平衡因子發(fā)現(xiàn)結(jié)點“31”的平衡因子為-2,該樹為非平衡樹,需要進(jìn)行調(diào)整,我們以結(jié)點“47”為旋轉(zhuǎn)中心
,將其父結(jié)點
按逆時針旋轉(zhuǎn)
為其左子樹
,至此該樹調(diào)整完畢。
注意
:47的右結(jié)點69不進(jìn)行旋轉(zhuǎn)。
(b)
圖b中我們插入“76”后計算平衡因子發(fā)現(xiàn)結(jié)點“31”的平衡因子為2,該樹為非平衡樹,需要進(jìn)行調(diào)整,我們以結(jié)點“47”為旋轉(zhuǎn)中心
,將其父結(jié)點“31”
按逆時針旋轉(zhuǎn)
為其左子樹
,再把旋轉(zhuǎn)中心“47”的左子樹
調(diào)整為31
的右子樹,至此該樹調(diào)整完畢。
?? 我們總結(jié)出以下規(guī)律
(參考下圖):將B
結(jié)點作為根結(jié)點,A
結(jié)點旋轉(zhuǎn)為B
結(jié)點的左子樹,再把B
結(jié)點本來的左子樹連接為A
結(jié)點的右子樹,該樹調(diào)整完成。
?? 可以看到LL型調(diào)整和RR型是左右相反的調(diào)整。
代碼實現(xiàn):
AVLTree RR_rotate(AVLTree T)
{
AVLTree parent=NULL,son;//son結(jié)點即為旋轉(zhuǎn)中心
parent=T->father;//獲取失衡結(jié)點的父節(jié)點
son=T->rchild;//獲取失衡結(jié)點的右孩子
if (son->lchild!=NULL)//設(shè)置son結(jié)點左孩子的父指針
son->lchild->father=T;
T->rchild=son->lchild;//失衡結(jié)點的右孩子變更為son的左孩子
//T的子結(jié)點更新完畢
count_depth(T);//更新失衡結(jié)點的高度信息
son->lchild=T;//失衡結(jié)點變成son的右孩子
son->father=parent;//設(shè)置son的父結(jié)點為原失衡結(jié)點的父結(jié)點,連接整顆樹
//如果失衡結(jié)點不是根結(jié)點,則更新父節(jié)點
if (parent!=NULL)
{
//如果父節(jié)點的左孩子是失衡結(jié)點,指向現(xiàn)在更新后的新孩子son
if (parent->lchild==T)
parent->lchild=son;
else //父節(jié)點的右孩子是失衡結(jié)點
parent->rchild=son;
}
T->father=son;//設(shè)置失衡結(jié)點的父親
count_depth(son);//更新son結(jié)點的高度信息
return son;
}
(3) LR型
(a)
圖a中我們插入“28” 后計算平衡因子發(fā)現(xiàn)結(jié)點“31”的平衡因子為2,該樹為非平衡樹,需要進(jìn)行調(diào)整。
(1)我們對25和28進(jìn)行RR型旋轉(zhuǎn)調(diào)整以結(jié)點“28”為旋轉(zhuǎn)中心
,將其父結(jié)點25
按逆時針旋轉(zhuǎn)
為其左子樹
,再連接為31的左子樹。
(2)觀察到調(diào)整后的樹是LL型,故以結(jié)點“28”為旋轉(zhuǎn)中心
,將其父結(jié)點31
按順時針旋轉(zhuǎn)
為其右子樹
,調(diào)整完畢。
(b)
圖b中我們插入“26” 后計算平衡因子發(fā)現(xiàn)結(jié)點“31”的平衡因子為2,該樹為非平衡樹,需要進(jìn)行調(diào)整。
(1)我們對25和28進(jìn)行RR型旋轉(zhuǎn)調(diào)整以結(jié)點“28”為旋轉(zhuǎn)中心
,將其父結(jié)點25
按逆時針旋轉(zhuǎn)
為其左子樹
,“28”的左子樹“26”
連接為“25”的右子樹,再將"28"連接為"31"的左子樹。
(2)觀察到調(diào)整后的樹是LL型,故以結(jié)點“28”為旋轉(zhuǎn)中心
,將其父結(jié)點31
按順時針旋轉(zhuǎn)
為其右子樹
,調(diào)整完畢。
(c)
圖b中我們插入“30” 后計算平衡因子發(fā)現(xiàn)結(jié)點“31”的平衡因子為2,該樹為非平衡樹,需要進(jìn)行調(diào)整。
(1)我們對25和28進(jìn)行RR型旋轉(zhuǎn)調(diào)整以結(jié)點“28”為旋轉(zhuǎn)中心
,將其父結(jié)點25
按逆時針旋轉(zhuǎn)
為其左子樹
,再將"28"連接為"31"的左子樹。
(2)觀察到調(diào)整后的樹是LL型,故以結(jié)點“28”為旋轉(zhuǎn)中心
,將其父結(jié)點31
按順時針旋轉(zhuǎn)
為其右子樹
,再將“28”(旋轉(zhuǎn)中心)的右子樹“30”連接為“31”的左子樹,調(diào)整完畢。
??我們總結(jié)出以下規(guī)律
(參考下圖):LR型先對B,C進(jìn)行RR型旋轉(zhuǎn)(以B為旋轉(zhuǎn)中心),旋轉(zhuǎn)后就變成LL型的非平衡樹之后再對A,B,C進(jìn)行LL型旋轉(zhuǎn)調(diào)整(以B為旋轉(zhuǎn)中心)。
代碼實現(xiàn):
AVLTree LR_rotate(AVLTree T)
{
RR_rotate(T->lchild);
return LL_rotate(T);
}
(4) RL型
RL型就與LR型相反,先進(jìn)行LL型旋轉(zhuǎn),再進(jìn)行RR型旋轉(zhuǎn)。如(a)圖的旋轉(zhuǎn)。
??我們總結(jié)出以下規(guī)律
(參考下圖):RL型先對B,C進(jìn)行LL型旋轉(zhuǎn)(以B為旋轉(zhuǎn)中心),旋轉(zhuǎn)后就變成RR型的非平衡樹之后再對A,B,C進(jìn)行RR型旋轉(zhuǎn)調(diào)整(以B為旋轉(zhuǎn)中心)。
代碼實現(xiàn):
AVLTree RL_rotate(AVLTree T)
{
LL_rotate(T->rchild);
return RR_rotate(T);
}
平衡二叉樹的創(chuàng)建
了解完了如何調(diào)整非平衡二叉樹,剩下工作就比較簡單了,無非是在創(chuàng)建二叉排序樹時,每次插入結(jié)點,把二叉樹調(diào)整為平衡二叉樹。
下面是代碼實現(xiàn):
輸入數(shù)據(jù)
void creativetree(AVLTree &T)//輸入數(shù)據(jù)
{
T=NULL;
int e,n,i;
scanf("%d",&n);
for (i=0; i
插入數(shù)據(jù)
AVLTree InsertAVL(AVLTree &T,int e,AVLTree parent)
{
if (!T)//找到結(jié)點插入位置
{
AVLNode *S;
S=new AVLNode;
S->data=e;
S->father=parent;
S->lchild=S->rchild=NULL;
T=S;
return S;
}
else if (edata)//向左搜索
{
return InsertAVL(T->lchild,e,T);
}
else if (e>T->data)//向右搜索
{
return InsertAVL(T->rchild,e,T);
}
return NULL;//該結(jié)點已存在
}
AVLTree Insert(AVLTree &T,int e)
{
AVLNode *S;
S=new AVLNode;
S=InsertAVL(T,e,NULL);//插入結(jié)點
count_depth(T);//更新深度信息
T=AVLchange(T,S);//調(diào)整為平衡二叉樹
return T;
}
調(diào)整平衡二叉樹
AVLTree AVLchange(AVLTree &T,AVLTree S)//調(diào)整為平衡二叉樹
{
int balance=0;//平衡因子
while (S)
{
count_depth(T);//計算深度
balance=count_balance(S);//計算平衡因子
if(balance>1||balance<-1)
{
if(balance>1)//L型
{
if(count_balance(S->lchild)>0)//LL型
{
S=LL_rotate(S);
}
else //LR型
{
S=LR_rotate(S);
}
}
if(balance<1)//R型
{
if (count_balance(S->rchild)<0) //RR型
S=RR_rotate(S);
else //RL型
S=RL_rotate(S);
}
if(S->father==NULL)//到達(dá)根節(jié)點,退出循環(huán)
{
T=S;//更新為新的根節(jié)點
break;
}
}
S=S->father;//一層一層往上調(diào)整
}
return T;
}
平衡二叉樹實現(xiàn)的整體代碼
#include "stdio.h"
#include "malloc.h"
#include
#include
#include
using namespace std;
typedef struct AVLNode
{
int data=0;//結(jié)點值
int depth=0;//深度
struct AVLNode *father=NULL;//父結(jié)點
struct AVLNode *lchild=NULL,*rchild=NULL;//左右結(jié)點
} AVLNode,*AVLTree; //結(jié)點結(jié)構(gòu)體
int count_depth(AVLTree &T)//計算各結(jié)點的深度
{
if(T==NULL)
{
return 0;
}
else
{
int l=count_depth(T->lchild);//左子樹深度
int r=count_depth(T->rchild);//右子樹深度
return T->depth=max(l,r)+1;//更新深度
}
}
int get_balance(AVLTree T)//讀取深度
{
if(T)
{
return T->depth;
}
else
{
return 0;
}
}
int count_balance(AVLTree T)//計算平衡因子
{
if(!T)
{
return 0;
}
else
{
return get_balance(T->lchild)-get_balance(T->rchild);//平衡因子等于左右子樹的深度之差
}
}
AVLTree LL_rotate(AVLTree T)
{
AVLTree parent=NULL,son;//son結(jié)點即為旋轉(zhuǎn)中心
parent=T->father;//獲取失衡結(jié)點的父節(jié)點
son=T->lchild;//獲取失衡結(jié)點的左孩子
if (son->rchild!=NULL)//設(shè)置son結(jié)點右孩子的父指針
son->rchild->father=T;
T->lchild=son->rchild;//失衡結(jié)點的左孩子變更為son的右孩子
//T的子結(jié)點更新完畢
count_depth(T);//更新失衡結(jié)點的深度信息
son->rchild=T;//失衡結(jié)點變成son的右孩子
son->father=parent;//設(shè)置son的父結(jié)點為原失衡結(jié)點的父結(jié)點,連接整顆樹
//如果失衡結(jié)點不是根結(jié)點,則更新父節(jié)點
if (parent!=NULL)
{
//如果父節(jié)點的左孩子是失衡結(jié)點,指向現(xiàn)在更新后的新孩子son
if (parent->lchild==T)
parent->lchild=son;
else //父節(jié)點的右孩子是失衡結(jié)點
parent->rchild=son;
}
T->father=son;//設(shè)置失衡結(jié)點的父親
count_depth(son);//更新son結(jié)點的高度信息
return son;
}
AVLTree RR_rotate(AVLTree T)
{
AVLTree parent=NULL,son;//son結(jié)點即為旋轉(zhuǎn)中心
parent=T->father;//獲取失衡結(jié)點的父節(jié)點
son=T->rchild;//獲取失衡結(jié)點的右孩子
if (son->lchild!=NULL)//設(shè)置son結(jié)點左孩子的父指針
son->lchild->father=T;
T->rchild=son->lchild;//失衡結(jié)點的右孩子變更為son的左孩子
//T的子結(jié)點更新完畢
count_depth(T);//更新失衡結(jié)點的高度信息
son->lchild=T;//失衡結(jié)點變成son的右孩子
son->father=parent;//設(shè)置son的父結(jié)點為原失衡結(jié)點的父結(jié)點,連接整顆樹
//如果失衡結(jié)點不是根結(jié)點,則更新父節(jié)點
if (parent!=NULL)
{
//如果父節(jié)點的左孩子是失衡結(jié)點,指向現(xiàn)在更新后的新孩子son
if (parent->lchild==T)
parent->lchild=son;
else //父節(jié)點的右孩子是失衡結(jié)點
parent->rchild=son;
}
T->father=son;//設(shè)置失衡結(jié)點的父親
count_depth(son);//更新son結(jié)點的高度信息
return son;
}
AVLTree LR_rotate(AVLTree T)
{
RR_rotate(T->lchild);
return LL_rotate(T);
}
AVLTree RL_rotate(AVLTree T)
{
LL_rotate(T->rchild);
return RR_rotate(T);
}
AVLTree AVLchange(AVLTree &T,AVLTree S)//調(diào)整為平衡二叉樹
{
int balance=0;//平衡因子
while (S)
{
count_depth(T);//計算深度
balance=count_balance(S);//計算平衡因子
if(balance>1||balance<-1)
{
if(balance>1)//L型
{
if(count_balance(S->lchild)>0)//LL型
{
S=LL_rotate(S);
}
else //LR型
{
S=LR_rotate(S);
}
}
if(balance<1)//R型
{
if (count_balance(S->rchild)<0) //RR型
S=RR_rotate(S);
else //RL型
S=RL_rotate(S);
}
if(S->father==NULL)//到達(dá)根節(jié)點,退出循環(huán)
{
T=S;//更新為新的根節(jié)點
break;
}
}
S=S->father;//一層一層往上調(diào)整
}
return T;
}
AVLTree InsertAVL(AVLTree &T,int e,AVLTree parent)
{
if (!T)//找到結(jié)點插入位置
{
AVLNode *S;
S=new AVLNode;
S->data=e;
S->father=parent;
S->lchild=S->rchild=NULL;
T=S;
return S;
}
else if (edata)//向左搜索
{
return InsertAVL(T->lchild,e,T);
}
else if (e>T->data)//向右搜索
{
return InsertAVL(T->rchild,e,T);
}
return NULL;//該結(jié)點已存在
}
AVLTree Insert(AVLTree &T,int e)
{
AVLNode *S;
S=new AVLNode;
S=InsertAVL(T,e,NULL);//插入結(jié)點
count_depth(T);//更新深度信息
T=AVLchange(T,S);//調(diào)整為平衡二叉樹
return T;
}
void creativetree(AVLTree &T)
{
T=NULL;
int e,n,i;
scanf("%d",&n);
for (i=0; i
平衡二叉樹的刪除操作和其他遍歷操作與二叉排序樹無異
,其他操作可以參考這里 實現(xiàn)二叉排序樹的各種算法
網(wǎng)站欄目:平衡二叉樹(AVL)的實現(xiàn)
文章來源:http://fisionsoft.com.cn/article/dsoigcj.html