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

歡迎訪問(wèn) 生活随笔!

生活随笔

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

编程问答

我这么讲线索二叉树,我三岁大的表弟笑了笑

發(fā)布時(shí)間:2024/10/14 编程问答 51 豆豆
生活随笔 收集整理的這篇文章主要介紹了 我这么讲线索二叉树,我三岁大的表弟笑了笑 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

目錄
1.線索二叉樹(shù)的由來(lái)
2.線索二叉樹(shù)的概念和結(jié)構(gòu)
3.二叉樹(shù)的線索化
4.中序線索二叉樹(shù)的代碼實(shí)現(xiàn)
5.遍歷線索二叉樹(shù)

1.線索二叉樹(shù)的由來(lái)

我問(wèn)表弟下面的一個(gè)問(wèn)題:

在n個(gè)結(jié)點(diǎn)一個(gè)二叉鏈表中,有多少個(gè)空指針域?

表弟一臉蒙蔽,于是我解釋道:既然有n個(gè)結(jié)點(diǎn),那么總共應(yīng)該有2n個(gè)指針域,我們的根結(jié)點(diǎn)是無(wú)結(jié)點(diǎn)指向的,其他的結(jié)點(diǎn)肯定都有一個(gè)結(jié)點(diǎn)指向,也就是說(shuō)一共用了n-1個(gè)指針域(根結(jié)點(diǎn)無(wú)結(jié)點(diǎn)指向所以需要減1),那么也就是說(shuō)還有n+1個(gè)指針域沒(méi)有使用,由此我們可以得出一個(gè)結(jié)論:

任何一顆含n個(gè)結(jié)點(diǎn)的二叉樹(shù)都有n+1個(gè)指針域是空指針域

那么這么多的空指針域造成浪費(fèi) ,表弟你心疼不,表弟:管我屁事
我:

為了解決這些浪費(fèi)的空指針域,我們引進(jìn)線索二叉樹(shù)

2.線索二叉樹(shù)的概念和結(jié)構(gòu)

2.1線索二叉樹(shù)的概念

表弟:你給我講講線索二叉樹(shù)唄
我:你以為我和你搞著玩?講就講come on baby

線索二叉樹(shù)實(shí)際上就是使用這些空指針域來(lái)存儲(chǔ)結(jié)點(diǎn)之間前趨和后繼關(guān)系的一種特殊的二叉樹(shù)。線索二叉樹(shù)中,如果結(jié)點(diǎn)有左子樹(shù),則lchild 指針域指向左孩子,否則lchild 指針域指向該結(jié)點(diǎn)的直接前趨;同樣,如果結(jié)點(diǎn)有右子樹(shù),則rchild 指針域指向右孩子,否則rchild 指針域指向該結(jié)點(diǎn)的直接后繼。

LTag 和RTag 為標(biāo)志域。實(shí)際上就是兩個(gè)布爾類型的變量:
LTag 值為0 時(shí),表示lchild 指針域指向的是該結(jié)點(diǎn)的左孩子;為1 時(shí),表示指向的是該結(jié)點(diǎn)的直接前趨結(jié)點(diǎn);
RTag 值為0 時(shí),表示rchild 指針域指向的是該結(jié)點(diǎn)的右孩子;為1 時(shí),表示指向的是該結(jié)點(diǎn)的直接后繼結(jié)點(diǎn)。

2.2線索二叉樹(shù)的結(jié)構(gòu)代碼實(shí)現(xiàn)

表弟:你這講的花里胡哨的,裸敲個(gè)線索二叉樹(shù)的結(jié)構(gòu)代碼我看看
我:你這是質(zhì)疑你表哥我的實(shí)力啊,上DEVC++

#define TElemType int//定義元素的數(shù)據(jù)類型 typedef struct BiThrNode{//線索二叉樹(shù)那穩(wěn)的是鏈?zhǔn)浇Y(jié)構(gòu)TElemType data;struct BiThrNode *lchild,*rchild;int LTag;int Rtag; }BiThrNode,*BiThrTree;

3.二叉樹(shù)的線索化

表弟:那到底怎樣建立一個(gè)線索二叉樹(shù)呢
我:別急啊,聽(tīng)我慢慢說(shuō)

將二叉樹(shù)轉(zhuǎn)化為線索二叉樹(shù),實(shí)質(zhì)上是在遍歷二叉樹(shù)的過(guò)程中,將二叉鏈表中的空指針改為指向直接前趨或者直接后繼的線索
??線索化的過(guò)程即為在遍歷的過(guò)程中修改空指針的過(guò)程。在遍歷過(guò)程中,如果當(dāng)前結(jié)點(diǎn)沒(méi)有左孩子,需要將該結(jié)點(diǎn)的lchild 指針指向遍歷過(guò)程中的前一個(gè)結(jié)點(diǎn),所以在遍歷過(guò)程中,設(shè)置一個(gè)指針(名為pre ),時(shí)刻指向當(dāng)前訪問(wèn)結(jié)點(diǎn)的前一個(gè)結(jié)點(diǎn)。

表弟:空指針域里存放這些前驅(qū)和后繼有啥用,僅僅就是為了不浪費(fèi),但是存放了沒(méi)有用的東西和浪費(fèi)不是差不多嗎
我:這個(gè)問(wèn)題問(wèn)的好,其實(shí)存放前驅(qū)和后繼的意義是:

線索二叉樹(shù)的建立過(guò)程其實(shí)就是提高遍歷二叉樹(shù)效率的過(guò)程

表弟:這話我TM聽(tīng)不懂,說(shuō)人話
我:臥槽你個(gè)**,多大點(diǎn)說(shuō)對(duì)表哥我說(shuō)臟話,你這是上天啊

表弟:可不是要上天,你趕緊講講剛那句話啥意思
我:其實(shí)你想,我們根據(jù)普通的二叉樹(shù)的某一個(gè)結(jié)點(diǎn)是不是只能找到它的左孩子和右孩子,我們想要知道某一個(gè)結(jié)點(diǎn)的前驅(qū)和后繼,是不是就要重新遍歷整個(gè)二叉樹(shù),也就是說(shuō)我們每一次想要知道某一個(gè)結(jié)點(diǎn)的前驅(qū)和后繼,都要遍歷一遍整個(gè)二叉樹(shù),但是我們?nèi)绻麆?chuàng)建的是線索二叉樹(shù),那么它的空指針域里就存放它的前驅(qū)和后繼的信息,遍歷起來(lái)就特別的方便,就不需要遍歷整棵二叉樹(shù),就大大提高了遍歷的效率。

注意我們以不同的方式遍歷二叉樹(shù)它的前驅(qū)和后繼也就不同,所以線索二叉樹(shù)又分為前序線索二叉樹(shù),中序線索二叉樹(shù),后續(xù)線索二叉樹(shù)

其實(shí)我們構(gòu)建一顆線索二叉樹(shù)的過(guò)程其實(shí)就是,等同是把一顆二叉樹(shù)變成一個(gè)雙向鏈表的過(guò)程,這怎么理解呢,下面是一顆二叉樹(shù)的模型

我們中序遍歷利用指向右孩子的空指針(指向后繼)

再利用指向左孩子的空指針(指向前驅(qū))

聯(lián)合起來(lái)就變成如下圖(是不是類似一個(gè)雙向鏈表)

4.詳解中序線索二叉樹(shù)

表弟:那怎么用代碼實(shí)現(xiàn)遍歷過(guò)程中讓一顆二叉樹(shù)變成線索二叉樹(shù)呢
我:我教你用遍歷中序二叉樹(shù)的方法構(gòu)造一個(gè)中序線索二叉樹(shù),看下面一段代碼

BiThrTree pre;//所以在遍歷過(guò)程中,設(shè)置一個(gè)全局變量pre,時(shí)刻指向當(dāng)前訪問(wèn)結(jié)點(diǎn)的前一個(gè)結(jié)點(diǎn)。 void InThreading(BiThrTree p)//以p為根的子樹(shù)中序線索化 {//如果當(dāng)前結(jié)點(diǎn)存在if (p){//遞歸當(dāng)前結(jié)點(diǎn)的左子樹(shù),進(jìn)行線索化InThreading(p->lchild); //如果當(dāng)前結(jié)點(diǎn)沒(méi)有左孩子,左標(biāo)志位設(shè)為1,左指針域指向上一結(jié)點(diǎn)preif (!p->lchild)//p的左孩子為空 {//前驅(qū)線索p->Ltag =1;//左孩子指針指向前驅(qū)p->lchild = pre; }elsep->LTag=0; //如果pre 沒(méi)有右孩子,右標(biāo)志位設(shè)為1,右指針域指向當(dāng)前結(jié)點(diǎn)。if (!pre->rchild){//后繼線索pre->Rtag =1;//前驅(qū)右孩子指針指向后繼(當(dāng)前結(jié)點(diǎn)p)pre->rchild =p;}else pre->RTag=0;pre = p; //pre 指向當(dāng)前結(jié)點(diǎn)InThreading(p->rchild); //遞歸右子樹(shù)進(jìn)行線索化} }

還有一種情況,那就是帶頭結(jié)點(diǎn)的中序線索二叉樹(shù)的構(gòu)建方法,那么什么是帶頭結(jié)點(diǎn)的二叉樹(shù)呢
我們還拿這顆二叉樹(shù)來(lái)說(shuō):

把上述二叉樹(shù)轉(zhuǎn)變成線索二叉樹(shù)后會(huì)發(fā)現(xiàn)H結(jié)點(diǎn)無(wú)前驅(qū),G結(jié)點(diǎn)無(wú)后繼,這時(shí)候引入一個(gè)頭結(jié)點(diǎn)

那么我們?cè)鯓泳€索化帶頭結(jié)點(diǎn)的線索二叉樹(shù)呢,看下面這一段代碼

//建立頭指針,使其左指針指向根結(jié)點(diǎn),右指針指向遍歷的最后一個(gè)結(jié)點(diǎn) void InOrder_Thr(BiThrTree &Thr,BiThrTree T)//中序遍歷二叉樹(shù)T,并將其中序線索化,Thr為頭指針指向頭結(jié)點(diǎn) {Thr=new BiThrNode; //建立一個(gè)頭結(jié)點(diǎn) Thr->LTag=0;//頭結(jié)點(diǎn)有左孩子若樹(shù)非空那么左孩子指向根結(jié)點(diǎn) Thr->RTag=1;//右孩子指針為右索引 Thr->rchild=Thr; //初始化的時(shí)候右指針指向自己 if(!T)//如果樹(shù)為空那么初始化的時(shí)候指針也指向自己 Thr->lchild=Thr;else{Thr->lchild=T;//頭結(jié)點(diǎn)左孩子指向根,pre初始值為頭結(jié)點(diǎn) InThreading(T);//對(duì)以T為根的二叉樹(shù)進(jìn)行線索化 pre->rchild=Thr;//中序線索化后pre為遍歷的最后結(jié)點(diǎn) ,讓最后的結(jié)點(diǎn)的右結(jié)點(diǎn)指向頭結(jié)點(diǎn) pre->RTag=1;Thr->rchild=pre;//頭結(jié)點(diǎn)的右索引指向pre } }

5.遍歷線索二叉樹(shù)

下圖中是一個(gè)按照中序遍歷建立的線索二叉樹(shù)。其中,實(shí)線表示指針,指向的是左孩子或者右孩子。虛線表示線索,指向的是該結(jié)點(diǎn)的直接前趨或者直接后繼。

??使用線索二叉樹(shù)時(shí),會(huì)經(jīng)常遇到一個(gè)問(wèn)題,如上圖中,結(jié)點(diǎn)8的直接后繼直接通過(guò)指針域獲得,為結(jié)點(diǎn)5;而由于結(jié)點(diǎn)5的度為2 ,無(wú)法利用指針域指向后繼結(jié)點(diǎn),整個(gè)鏈表斷掉了。當(dāng)在遍歷過(guò)程,遇到這種問(wèn)題是解決的辦法就是:尋找先序、中序、后序遍歷的規(guī)律,找到下一個(gè)結(jié)點(diǎn)。

???在先序遍歷過(guò)程中,如果結(jié)點(diǎn)因?yàn)橛杏液⒆訉?dǎo)致無(wú)法找到其后繼結(jié)點(diǎn),如果結(jié)點(diǎn)有左孩子,則后繼結(jié)點(diǎn)是其左孩子;否則,就一定是右孩子。拿上圖舉例,結(jié)點(diǎn)2的后繼結(jié)點(diǎn)是其左孩子結(jié)點(diǎn)4 ,如果結(jié)點(diǎn)4不存在的話,就是結(jié)點(diǎn)5 。
??在中序遍歷過(guò)程中,結(jié)點(diǎn)的后繼是遍歷其右子樹(shù)時(shí)訪問(wèn)的第一個(gè)結(jié)點(diǎn),也就是右子樹(shù)中位于最左下的結(jié)點(diǎn)。例如上圖中結(jié)點(diǎn)5,后繼結(jié)點(diǎn)為結(jié)點(diǎn)11,是其右子樹(shù)中位于最左邊的結(jié)點(diǎn)。反之,結(jié)點(diǎn)的前趨是左子樹(shù)最后訪問(wèn)的那個(gè)結(jié)點(diǎn)。
??后序遍歷中找后繼結(jié)點(diǎn)需要分為3 種情況:
??1. 如果該結(jié)點(diǎn)是二叉樹(shù)的根,后繼結(jié)點(diǎn)為空;
??2. 如果該結(jié)點(diǎn)是父結(jié)點(diǎn)的右孩子(或者是左孩子,但是父結(jié)點(diǎn)沒(méi)有右孩子),后繼結(jié)點(diǎn)是父結(jié)點(diǎn);
??3. 如果該結(jié)點(diǎn)是父結(jié)點(diǎn)的左孩子,且父結(jié)點(diǎn)有右子樹(shù),后繼結(jié)點(diǎn)為父結(jié)點(diǎn)的右子樹(shù)在后序遍歷列出的第一個(gè)結(jié)點(diǎn)。
??使用后序遍歷建立的線索二叉樹(shù),在真正使用過(guò)程中遇到鏈表的斷點(diǎn)時(shí),需要訪問(wèn)父結(jié)點(diǎn),所以在初步建立二叉樹(shù)時(shí),宜采用三叉鏈表做存儲(chǔ)結(jié)構(gòu)。

表弟:這么多,勸退?
我:我們重點(diǎn)掌握中序的就行,下面我實(shí)現(xiàn)一下中序線索二叉樹(shù)的遍歷的非遞歸代碼你看看

void InOrderTraverse_Thr(BiThrTree T) {//T為頭指針,頭結(jié)點(diǎn)的左鏈lchild指向根結(jié)點(diǎn)BiThrTree p=T->lchild;//p指向根結(jié)點(diǎn) while(p!=T)//空樹(shù)或者遍歷結(jié)束時(shí)p==T {while(p->Ltag==0)p=p->lchild;//沿著左孩子向下 cout<<p->data;//訪問(wèn)左子樹(shù)為空的結(jié)點(diǎn) while(p->Rtag==1&&p->rchild!=T){p=p->rchild;//沿著右線索訪問(wèn)后繼結(jié)點(diǎn) cout<<p->data;}p=p->rchild;//轉(zhuǎn)向p的右子樹(shù) } }

結(jié)合下面圖一起看,下面圖的中序遍歷結(jié)果時(shí)4 2 8 5 11 9 12 1 6 3 7

總結(jié)

以上是生活随笔為你收集整理的我这么讲线索二叉树,我三岁大的表弟笑了笑的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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