通宵爆肝:C语言下的平衡二叉树(Avl)原来如此简单!
文章目錄
- 平衡二叉樹(shù)的構(gòu)造過(guò)程
- 1 算法描述
- 平衡二叉樹(shù)的編程
- 1 樹(shù)上結(jié)點(diǎn)的高度計(jì)算
- 2 LL調(diào)整函數(shù)
- 3 RR調(diào)整函數(shù)
- 4 LR調(diào)整函數(shù)
- 5 RL調(diào)整函數(shù)
- 6 根據(jù)結(jié)點(diǎn)的值、動(dòng)態(tài)構(gòu)造平衡二叉樹(shù)
平衡二叉樹(shù)的構(gòu)造過(guò)程
對(duì)一個(gè)查找問(wèn)題而言,查找表的存儲(chǔ)結(jié)構(gòu)、應(yīng)該組織成二叉樹(shù)結(jié)構(gòu)。而把一個(gè)離散的數(shù)據(jù)、組織成最接近滿二叉排序樹(shù)的方法,最常見(jiàn)的就是平衡二叉樹(shù)。
對(duì)平衡二叉樹(shù)而言,有四種調(diào)整方式,就是LL、LR、RR、RL四種方式。
1 算法描述
(1) LL調(diào)整
對(duì)圖1而言,如插入10,就是下面的過(guò)程:
插入的結(jié)果就是:如40為根,則左子樹(shù)高、減去右子樹(shù)、高度差是2。在這種情況下,需要調(diào)整,調(diào)整的結(jié)果就是:注意K1、K2結(jié)點(diǎn)的變化。
(2) RR調(diào)整
對(duì)下面的樹(shù)而言,插入80則發(fā)生RR調(diào)整:
對(duì)上述二叉樹(shù)、進(jìn)行調(diào)整就是下圖的過(guò)程:注意K1、K2結(jié)點(diǎn)的變化。
(3) LR調(diào)整
對(duì)以下的圖,如果插入35,則要進(jìn)行LR調(diào)整:
注意下面K3的變化:
.
(4) RL調(diào)整
在以下的樹(shù)上,如果要插入53,則要進(jìn)行的調(diào)整就是RL調(diào)整,注意過(guò)程:
此時(shí)要調(diào)整的過(guò)程、類(lèi)似LR調(diào)整,過(guò)程如下:注意K1的變化。
平衡二叉樹(shù)的四種調(diào)整介紹完畢,你能就任意一組數(shù)據(jù)、完成構(gòu)造平衡二叉樹(shù)的過(guò)程么?
平衡二叉樹(shù)的編程
1 樹(shù)上結(jié)點(diǎn)的高度計(jì)算
從前面的平衡二叉樹(shù)的構(gòu)造過(guò)程可知:對(duì)平衡二叉樹(shù),最關(guān)鍵的問(wèn)題是結(jié)點(diǎn)的高度計(jì)算問(wèn)題,如果每個(gè)結(jié)點(diǎn)的高度有了,則左右子樹(shù)的高度差也就可以計(jì)算了。
我們從二叉排序樹(shù)的結(jié)構(gòu)中補(bǔ)充一個(gè)字段:Height,同時(shí)修改結(jié)構(gòu)名稱(chēng)為AvlNode,目的就是構(gòu)造平衡二叉樹(shù)。
struct AvlNode{int Element;struct AvlNode *Left;struct AvlNode *Right;int Height;};我們定義:當(dāng)前結(jié)點(diǎn)的高度、是取左右孩子高度最大值再加1,所以對(duì)一下的結(jié)點(diǎn),有:
注意這個(gè)高度編法,它是從樹(shù)的葉結(jié)點(diǎn)開(kāi)始的,這樣的高度計(jì)算方法和從樹(shù)根編下來(lái)有差異,但計(jì)算左右樹(shù)高度差都是一樣的。之所以這么編高度,是因?yàn)榫幊讨写_定葉結(jié)點(diǎn)高度為0非常簡(jiǎn)單。
在表1的第7行,我們新構(gòu)造的每個(gè)結(jié)點(diǎn)高度都是0;
在表1的第11行,當(dāng)前結(jié)點(diǎn)T的高度是T的左、右孩子結(jié)點(diǎn)高度最大者加1,這樣構(gòu)造的樹(shù)、其每個(gè)結(jié)點(diǎn)的高度就如同表9。
能插入結(jié)點(diǎn)值為X并構(gòu)造二叉排序樹(shù)的程序見(jiàn)AvlTree0.c,它能給每個(gè)結(jié)點(diǎn)計(jì)算高度,當(dāng)然,這個(gè)程序還不能構(gòu)造平衡二叉樹(shù),它依然構(gòu)造的是二叉排序樹(shù)。但我們知道:平衡二叉樹(shù)是二叉排序樹(shù)的一種特殊情況。我們就要根據(jù)這個(gè)程序,不斷修改出一個(gè)能構(gòu)造平衡二叉樹(shù)的程序。
2 LL調(diào)整函數(shù)
LL調(diào)整函數(shù)的過(guò)程見(jiàn)圖1、圖2,首先我們給這個(gè)樹(shù)從葉結(jié)點(diǎn)開(kāi)始編高度,其高度數(shù)據(jù)就是:
為測(cè)試方便,我們先編寫(xiě)main()函數(shù)、構(gòu)造圖1這樣的二叉排序樹(shù),然后再編LL調(diào)整函數(shù),所以main()就是:
有這個(gè)二叉樹(shù)后再次回顧圖2,所謂LL調(diào)整就是:
如樹(shù)的根為K2,則:
K1為K2的左孩子;
K2的左孩子賦值為K1的右孩子;(35給40當(dāng)左孩子)
K1的右孩子賦值為K2;(40是35的右孩子)
調(diào)整K2的高度;
調(diào)整K1的高度;
返回K1為根的樹(shù);
用C語(yǔ)言寫(xiě)出來(lái)就是:
struct AvlNode *LL(struct AvlNode *K2) { struct AvlNode *K1; K1 = K2->Left; K2->Left = K1->Right; K1->Right = K2; //注意各個(gè)結(jié)點(diǎn)高度計(jì)算 K2->Height = Max(Height(K2->Left), Height(K2->Right))+1; K1->Height = Max( Height(K1->Left), K2->Height)+1; return K1; }全部代碼見(jiàn)LL.C,以下結(jié)果看看是否合理。
ID PID Value Height
0 -1 30 2
1 0 20 1
2 1 10 0
3 0 40 1
4 3 35 0
5 3 50 0
一定嘗試著用遍歷的結(jié)果來(lái)反推這個(gè)樹(shù)的形態(tài)。
3 RR調(diào)整函數(shù)
RR調(diào)整和LL調(diào)整是對(duì)稱(chēng)操作,看著圖4,所以是:
如K1是根結(jié)點(diǎn);
K2是K1的右孩子;
把K2的左孩子賦值給K1當(dāng)右孩子(53成為50的右孩子);
K1成為K2的左孩子(50成為60的左孩子);
重新計(jì)算K1的高度;
重新計(jì)算K2的高度;
返回K2為根的二叉樹(shù);
所以程序見(jiàn)下表:
struct AvlNode * RR(struct AvlNode *K1) { struct AvlNode *K2; K2 = K1->Right; K1->Right = K2->Left; K2->Left = K1;K1->Height = Max(Height(K1->Left), Height(K1->Right))+1; K2->Height = Max(Height(K2->Right), K1->Height)+1; return K2; }對(duì)照表4,可知和LL調(diào)整是完全向?qū)?yīng)、但方向相反。
下面是測(cè)試圖4中RR調(diào)整的二叉樹(shù)數(shù)據(jù)和main()程序
main( ) {struct AvlNode *T;struct AvlNode BT[6];/* 下面這組數(shù)據(jù)是測(cè)試RR平衡的 */BT[0].Element=50;BT[0].Left=&BT[1];BT[0].Right=&BT[2];BT[0].Height=3; BT[1].Element=40;BT[1].Left=NULL; BT[1].Right=NULL; BT[1].Height=0; BT[2].Element=60;BT[2].Left=&BT[3];BT[2].Right=&BT[4];BT[2].Height=2; BT[3].Element=53;BT[3].Left=NULL; BT[3].Right=NULL; BT[3].Height=0; BT[4].Element=70;BT[4].Left=NULL; BT[4].Right=&BT[5];BT[4].Height=1; BT[5].Element=80;BT[5].Left=NULL; BT[5].Right=NULL;BT[5].Height=0; T=RR(BT);jConvert(T);printf("\n"); }這個(gè)程序的結(jié)果見(jiàn)下表:
仔細(xì)分析以下這些結(jié)點(diǎn)的關(guān)系,嘗試著用遍歷的方法反推這個(gè)樹(shù)的形態(tài)。
4 LR調(diào)整函數(shù)
從圖6的過(guò)程可以看到:
所謂LR調(diào)整、如K3為根的話,則K3的左孩子先進(jìn)行RR調(diào)整,然后,整個(gè)樹(shù)再以K3為根做LL調(diào)整。
完成這樣的調(diào)整、寫(xiě)成程序會(huì)非常簡(jiǎn)單,就是:
每個(gè)調(diào)整過(guò)程都會(huì)自己調(diào)整結(jié)點(diǎn)高度,所以整個(gè)LR調(diào)整中不需要再次調(diào)整結(jié)點(diǎn)高度。
5 RL調(diào)整函數(shù)
從圖8的過(guò)程可知:
如樹(shù)的根結(jié)點(diǎn)是K1,則首先K1的右孩子進(jìn)行LL調(diào)整,調(diào)整后的結(jié)果,再按K1為根進(jìn)行RR調(diào)整。
所以整個(gè)RL的調(diào)整編程就是:
LR.C、LR.C兩個(gè)函數(shù)的具體執(zhí)行情況見(jiàn)相關(guān)程序。各個(gè)程序的結(jié)果請(qǐng)同學(xué)們自己分析。這樣,我們就完成了平衡二叉樹(shù)的結(jié)點(diǎn)高度定義、以及四種調(diào)整方法的函數(shù),有了這些基礎(chǔ),我們?cè)俅涡薷腎nsert()函數(shù)就有基礎(chǔ)了。
6 根據(jù)結(jié)點(diǎn)的值、動(dòng)態(tài)構(gòu)造平衡二叉樹(shù)
回顧表1,這個(gè)函數(shù)僅僅能構(gòu)造二叉排序樹(shù),經(jīng)過(guò)修改,目前能對(duì)插入的結(jié)點(diǎn)計(jì)算出高度來(lái),我們要不斷修改這個(gè)函數(shù),讓它能構(gòu)造平衡二叉樹(shù)。
首先,當(dāng)插入X后,要構(gòu)造出二叉排序樹(shù),其次要立刻判斷這個(gè)結(jié)點(diǎn)的高度差,在表1中,在當(dāng)前結(jié)點(diǎn)T上插入結(jié)點(diǎn)(以插入左子樹(shù)左孩子為例)后,就是:
if(X<T->Element) T->Left=Insert(X,T->Left);但現(xiàn)在,首先要計(jì)算T的左右孩子高度差,如果高度差是2,則再次判斷X是否小于T的左孩子,如是,則必然為L(zhǎng)L調(diào)整,否則為L(zhǎng)R調(diào)整,就是:
if(X<T->Element) {T->Left = Insert(X,T->Left);if(Height(T->Left)-Height(T->Right)==2)if(X<T->Left->Element)T=LL(T);elseT=LR(T); }注意上面的過(guò)程,是在X插入到當(dāng)前結(jié)點(diǎn)T的左孩子的情況,此時(shí),還有兩種可能,在第5行的判斷就是:插入到T的左孩子左子樹(shù),則做LL調(diào)整,如是在左孩子右子樹(shù),則做LR調(diào)整。同理可以編寫(xiě)出插入到右子樹(shù)的情況,整個(gè)平衡二叉樹(shù)的構(gòu)造函數(shù)就是:
struct AvlNode *Insert(int X, struct AvlNode *T) { if(T==NULL){T =(struct AvlNode *)malloc(sizeof(struct AvlNode));if(T==NULL) {printf("內(nèi)存不夠,程序退出" );exit (0);}T->Element=X; T->Height=0;T->Left=T->Right= NULL;}if(X<T->Element){T->Left = Insert(X,T->Left);if(Height(T->Left)-Height(T->Right)==2)if(X<T->Left->Element)T=LL(T);elseT=LR(T);}if(X>T->Element){T->Right = Insert(X,T->Right);if(Height(T->Right)-Height(T->Left)== 2)if(X>T->Right->Element )T=RR(T);elseT=RL(T);} T->Height=Max(Height(T->Left), Height(T->Right))+1; return (T); }有這個(gè)函數(shù)后,我們可以編寫(xiě)個(gè)main()函數(shù),測(cè)試這個(gè)函數(shù)是否正確,整個(gè)程序見(jiàn)AvlTree.c,這個(gè)程序用遍歷的方法給出樹(shù)的形態(tài),注意自己再反推回去。
總結(jié)
以上是生活随笔為你收集整理的通宵爆肝:C语言下的平衡二叉树(Avl)原来如此简单!的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【专升本计算机】甘肃省专升本计算机基础-
- 下一篇: 【ArcGIS Pro微课1000例】0