我这么讲线索二叉树,我三岁大的表弟笑了笑
目錄:
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++
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ù),看下面一段代碼
還有一種情況,那就是帶頭結(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ù)的遍歷的非遞歸代碼你看看
結(jié)合下面圖一起看,下面圖的中序遍歷結(jié)果時(shí)4 2 8 5 11 9 12 1 6 3 7
總結(jié)
以上是生活随笔為你收集整理的我这么讲线索二叉树,我三岁大的表弟笑了笑的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 循环队列之舞伴问题(含源码详解)
- 下一篇: 筛选法求素数