日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

通宵爆肝:C语言下的平衡二叉树(Avl)原来如此简单!

發(fā)布時(shí)間:2023/12/4 编程问答 53 豆豆
生活随笔 收集整理的這篇文章主要介紹了 通宵爆肝:C语言下的平衡二叉树(Avl)原来如此简单! 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • 平衡二叉樹的構(gòu)造過程
    • 1 算法描述
  • 平衡二叉樹的編程
    • 1 樹上結(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)造平衡二叉樹


平衡二叉樹的構(gòu)造過程

對(duì)一個(gè)查找問題而言,查找表的存儲(chǔ)結(jié)構(gòu)、應(yīng)該組織成二叉樹結(jié)構(gòu)。而把一個(gè)離散的數(shù)據(jù)、組織成最接近滿二叉排序樹的方法,最常見的就是平衡二叉樹。

對(duì)平衡二叉樹而言,有四種調(diào)整方式,就是LL、LR、RR、RL四種方式。

1 算法描述

(1) LL調(diào)整

對(duì)圖1而言,如插入10,就是下面的過程:

插入的結(jié)果就是:如40為根,則左子樹高、減去右子樹、高度差是2。在這種情況下,需要調(diào)整,調(diào)整的結(jié)果就是:注意K1、K2結(jié)點(diǎn)的變化。


(2) RR調(diào)整

對(duì)下面的樹而言,插入80則發(fā)生RR調(diào)整:

對(duì)上述二叉樹、進(jìn)行調(diào)整就是下圖的過程:注意K1、K2結(jié)點(diǎn)的變化。


(3) LR調(diào)整

對(duì)以下的圖,如果插入35,則要進(jìn)行LR調(diào)整:


注意下面K3的變化:

.
(4) RL調(diào)整

在以下的樹上,如果要插入53,則要進(jìn)行的調(diào)整就是RL調(diào)整,注意過程:


此時(shí)要調(diào)整的過程、類似LR調(diào)整,過程如下:注意K1的變化。

平衡二叉樹的四種調(diào)整介紹完畢,你能就任意一組數(shù)據(jù)、完成構(gòu)造平衡二叉樹的過程么?

平衡二叉樹的編程

1 樹上結(jié)點(diǎn)的高度計(jì)算

從前面的平衡二叉樹的構(gòu)造過程可知:對(duì)平衡二叉樹,最關(guān)鍵的問題是結(jié)點(diǎn)的高度計(jì)算問題,如果每個(gè)結(jié)點(diǎn)的高度有了,則左右子樹的高度差也就可以計(jì)算了。

我們從二叉排序樹的結(jié)構(gòu)中補(bǔ)充一個(gè)字段:Height,同時(shí)修改結(jié)構(gòu)名稱為AvlNode,目的就是構(gòu)造平衡二叉樹。

struct AvlNode{int Element;struct AvlNode *Left;struct AvlNode *Right;int Height;};

我們定義:當(dāng)前結(jié)點(diǎn)的高度、是取左右孩子高度最大值再加1,所以對(duì)一下的結(jié)點(diǎn),有:


注意這個(gè)高度編法,它是從樹的葉結(jié)點(diǎn)開始的,這樣的高度計(jì)算方法和從樹根編下來有差異,但計(jì)算左右樹高度差都是一樣的。之所以這么編高度,是因?yàn)榫幊讨写_定葉結(jié)點(diǎn)高度為0非常簡單。

struct AvlNode *Insert(int X, struct AvlNode *T) {if(T==NULL){T =(struct AvlNode *)malloc(sizeof(struct AvlNode));if(T==NULL) {printf("內(nèi)存不足\n");exit(0);}T->Element=X; T->Left=T->Right= NULL;T->Height=0;}if(X<T->Element) T->Left=Insert(X,T->Left);if(X>T->Element) T->Right=Insert(X,T->Right);T->Height=Max(Height(T->Left), Height(T->Right))+1; return T; }

在表1的第7行,我們新構(gòu)造的每個(gè)結(jié)點(diǎn)高度都是0;

在表1的第11行,當(dāng)前結(jié)點(diǎn)T的高度是T的左、右孩子結(jié)點(diǎn)高度最大者加1,這樣構(gòu)造的樹、其每個(gè)結(jié)點(diǎn)的高度就如同表9。

能插入結(jié)點(diǎn)值為X并構(gòu)造二叉排序樹的程序見AvlTree0.c,它能給每個(gè)結(jié)點(diǎn)計(jì)算高度,當(dāng)然,這個(gè)程序還不能構(gòu)造平衡二叉樹,它依然構(gòu)造的是二叉排序樹。但我們知道:平衡二叉樹是二叉排序樹的一種特殊情況。我們就要根據(jù)這個(gè)程序,不斷修改出一個(gè)能構(gòu)造平衡二叉樹的程序。

2 LL調(diào)整函數(shù)

LL調(diào)整函數(shù)的過程見圖1、圖2,首先我們給這個(gè)樹從葉結(jié)點(diǎn)開始編高度,其高度數(shù)據(jù)就是:


為測試方便,我們先編寫main()函數(shù)、構(gòu)造圖1這樣的二叉排序樹,然后再編LL調(diào)整函數(shù),所以main()就是:

main( ) {struct AvlNode *T;struct AvlNode BT[6];/* 構(gòu)造圖1的二叉排序樹 */BT[0].Element=40;BT[0].Left=&BT[1];BT[0].Right=&BT[2];BT[0].Height=3;BT[1].Element=30;BT[1].Left=&BT[3];BT[1].Right=&BT[4];BT[1].Height=2;BT[2].Element=50;BT[2].Left=NULL; BT[2].Right=NULL;BT[2].Height=0; BT[3].Element=20;BT[3].Left=&BT[5];BT[3].Right=NULL;BT[3].Height=1; BT[4].Element=35;BT[4].Left=NULL; BT[4].Right=NULL;BT[4].Height=0; BT[5].Element=10;BT[5].Left=NULL; BT[5].Right=NULL;BT[5].Height=0; T=LL(BT);jConvert(T);printf("\n"); }

有這個(gè)二叉樹后再次回顧圖2,所謂LL調(diào)整就是:

如樹的根為K2,則:

K1為K2的左孩子;

K2的左孩子賦值為K1的右孩子;(35給40當(dāng)左孩子)

K1的右孩子賦值為K2;(40是35的右孩子)

調(diào)整K2的高度;

調(diào)整K1的高度;

返回K1為根的樹;

用C語言寫出來就是:

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; }

全部代碼見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é)果來反推這個(gè)樹的形態(tài)。

3 RR調(diào)整函數(shù)

RR調(diào)整和LL調(diào)整是對(duì)稱操作,看著圖4,所以是:

如K1是根結(jié)點(diǎn);

K2是K1的右孩子;

把K2的左孩子賦值給K1當(dāng)右孩子(53成為50的右孩子);

K1成為K2的左孩子(50成為60的左孩子);

重新計(jì)算K1的高度;

重新計(jì)算K2的高度;

返回K2為根的二叉樹;

所以程序見下表:

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)、但方向相反。

下面是測試圖4中RR調(diào)整的二叉樹數(shù)據(jù)和main()程序

main( ) {struct AvlNode *T;struct AvlNode BT[6];/* 下面這組數(shù)據(jù)是測試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é)果見下表:


仔細(xì)分析以下這些結(jié)點(diǎn)的關(guān)系,嘗試著用遍歷的方法反推這個(gè)樹的形態(tài)。

4 LR調(diào)整函數(shù)

從圖6的過程可以看到:

所謂LR調(diào)整、如K3為根的話,則K3的左孩子先進(jìn)行RR調(diào)整,然后,整個(gè)樹再以K3為根做LL調(diào)整。
完成這樣的調(diào)整、寫成程序會(huì)非常簡單,就是:

struct AvlNode *LR(struct AvlNode *K3) {K3->Left = RR(K3->Left);return LL(K3); }

每個(gè)調(diào)整過程都會(huì)自己調(diào)整結(jié)點(diǎn)高度,所以整個(gè)LR調(diào)整中不需要再次調(diào)整結(jié)點(diǎn)高度。

5 RL調(diào)整函數(shù)

從圖8的過程可知:

如樹的根結(jié)點(diǎn)是K1,則首先K1的右孩子進(jìn)行LL調(diào)整,調(diào)整后的結(jié)果,再按K1為根進(jìn)行RR調(diào)整。
所以整個(gè)RL的調(diào)整編程就是:

struct AvlNode *RL(struct AvlNode *K1) { K1->Right=LL(K1->Right); return RR(K1); }

LR.C、LR.C兩個(gè)函數(shù)的具體執(zhí)行情況見相關(guān)程序。各個(gè)程序的結(jié)果請(qǐng)同學(xué)們自己分析。這樣,我們就完成了平衡二叉樹的結(jié)點(diǎn)高度定義、以及四種調(diào)整方法的函數(shù),有了這些基礎(chǔ),我們?cè)俅涡薷腎nsert()函數(shù)就有基礎(chǔ)了。

6 根據(jù)結(jié)點(diǎn)的值、動(dòng)態(tài)構(gòu)造平衡二叉樹

回顧表1,這個(gè)函數(shù)僅僅能構(gòu)造二叉排序樹,經(jīng)過修改,目前能對(duì)插入的結(jié)點(diǎn)計(jì)算出高度來,我們要不斷修改這個(gè)函數(shù),讓它能構(gòu)造平衡二叉樹。

首先,當(dāng)插入X后,要構(gòu)造出二叉排序樹,其次要立刻判斷這個(gè)結(jié)點(diǎn)的高度差,在表1中,在當(dāng)前結(jié)點(diǎn)T上插入結(jié)點(diǎn)(以插入左子樹左孩子為例)后,就是:

if(X<T->Element) T->Left=Insert(X,T->Left);

但現(xiàn)在,首先要計(jì)算T的左右孩子高度差,如果高度差是2,則再次判斷X是否小于T的左孩子,如是,則必然為LL調(diào)整,否則為LR調(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); }

注意上面的過程,是在X插入到當(dāng)前結(jié)點(diǎn)T的左孩子的情況,此時(shí),還有兩種可能,在第5行的判斷就是:插入到T的左孩子左子樹,則做LL調(diào)整,如是在左孩子右子樹,則做LR調(diào)整。同理可以編寫出插入到右子樹的情況,整個(gè)平衡二叉樹的構(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ù)后,我們可以編寫個(gè)main()函數(shù),測試這個(gè)函數(shù)是否正確,整個(gè)程序見AvlTree.c,這個(gè)程序用遍歷的方法給出樹的形態(tài),注意自己再反推回去。

總結(jié)

以上是生活随笔為你收集整理的通宵爆肝:C语言下的平衡二叉树(Avl)原来如此简单!的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。