我这么讲线索二叉树,我三岁大的表弟笑了笑
目錄:
1.線索二叉樹的由來
2.線索二叉樹的概念和結構
3.二叉樹的線索化
4.中序線索二叉樹的代碼實現
5.遍歷線索二叉樹
1.線索二叉樹的由來
我問表弟下面的一個問題:
在n個結點一個二叉鏈表中,有多少個空指針域?
表弟一臉蒙蔽,于是我解釋道:既然有n個結點,那么總共應該有2n個指針域,我們的根結點是無結點指向的,其他的結點肯定都有一個結點指向,也就是說一共用了n-1個指針域(根結點無結點指向所以需要減1),那么也就是說還有n+1個指針域沒有使用,由此我們可以得出一個結論:
任何一顆含n個結點的二叉樹都有n+1個指針域是空指針域
那么這么多的空指針域造成浪費 ,表弟你心疼不,表弟:管我屁事
我:
為了解決這些浪費的空指針域,我們引進線索二叉樹
2.線索二叉樹的概念和結構
2.1線索二叉樹的概念
表弟:你給我講講線索二叉樹唄
我:你以為我和你搞著玩?講就講come on baby
線索二叉樹實際上就是使用這些空指針域來存儲結點之間前趨和后繼關系的一種特殊的二叉樹。線索二叉樹中,如果結點有左子樹,則lchild 指針域指向左孩子,否則lchild 指針域指向該結點的直接前趨;同樣,如果結點有右子樹,則rchild 指針域指向右孩子,否則rchild 指針域指向該結點的直接后繼。
LTag 和RTag 為標志域。實際上就是兩個布爾類型的變量:
LTag 值為0 時,表示lchild 指針域指向的是該結點的左孩子;為1 時,表示指向的是該結點的直接前趨結點;
RTag 值為0 時,表示rchild 指針域指向的是該結點的右孩子;為1 時,表示指向的是該結點的直接后繼結點。
2.2線索二叉樹的結構代碼實現
表弟:你這講的花里胡哨的,裸敲個線索二叉樹的結構代碼我看看
我:你這是質疑你表哥我的實力啊,上DEVC++
3.二叉樹的線索化
表弟:那到底怎樣建立一個線索二叉樹呢
我:別急啊,聽我慢慢說
將二叉樹轉化為線索二叉樹,實質上是在遍歷二叉樹的過程中,將二叉鏈表中的空指針改為指向直接前趨或者直接后繼的線索。
??線索化的過程即為在遍歷的過程中修改空指針的過程。在遍歷過程中,如果當前結點沒有左孩子,需要將該結點的lchild 指針指向遍歷過程中的前一個結點,所以在遍歷過程中,設置一個指針(名為pre ),時刻指向當前訪問結點的前一個結點。
表弟:空指針域里存放這些前驅和后繼有啥用,僅僅就是為了不浪費,但是存放了沒有用的東西和浪費不是差不多嗎
我:這個問題問的好,其實存放前驅和后繼的意義是:
線索二叉樹的建立過程其實就是提高遍歷二叉樹效率的過程
表弟:這話我TM聽不懂,說人話
我:臥槽你個**,多大點說對表哥我說臟話,你這是上天啊
表弟:可不是要上天,你趕緊講講剛那句話啥意思
我:其實你想,我們根據普通的二叉樹的某一個結點是不是只能找到它的左孩子和右孩子,我們想要知道某一個結點的前驅和后繼,是不是就要重新遍歷整個二叉樹,也就是說我們每一次想要知道某一個結點的前驅和后繼,都要遍歷一遍整個二叉樹,但是我們如果創建的是線索二叉樹,那么它的空指針域里就存放它的前驅和后繼的信息,遍歷起來就特別的方便,就不需要遍歷整棵二叉樹,就大大提高了遍歷的效率。
注意我們以不同的方式遍歷二叉樹它的前驅和后繼也就不同,所以線索二叉樹又分為前序線索二叉樹,中序線索二叉樹,后續線索二叉樹
其實我們構建一顆線索二叉樹的過程其實就是,等同是把一顆二叉樹變成一個雙向鏈表的過程,這怎么理解呢,下面是一顆二叉樹的模型
我們中序遍歷利用指向右孩子的空指針(指向后繼)
再利用指向左孩子的空指針(指向前驅)
聯合起來就變成如下圖(是不是類似一個雙向鏈表)
4.詳解中序線索二叉樹
表弟:那怎么用代碼實現遍歷過程中讓一顆二叉樹變成線索二叉樹呢
我:我教你用遍歷中序二叉樹的方法構造一個中序線索二叉樹,看下面一段代碼
還有一種情況,那就是帶頭結點的中序線索二叉樹的構建方法,那么什么是帶頭結點的二叉樹呢
我們還拿這顆二叉樹來說:
把上述二叉樹轉變成線索二叉樹后會發現H結點無前驅,G結點無后繼,這時候引入一個頭結點
那么我們怎樣線索化帶頭結點的線索二叉樹呢,看下面這一段代碼
//建立頭指針,使其左指針指向根結點,右指針指向遍歷的最后一個結點 void InOrder_Thr(BiThrTree &Thr,BiThrTree T)//中序遍歷二叉樹T,并將其中序線索化,Thr為頭指針指向頭結點 {Thr=new BiThrNode; //建立一個頭結點 Thr->LTag=0;//頭結點有左孩子若樹非空那么左孩子指向根結點 Thr->RTag=1;//右孩子指針為右索引 Thr->rchild=Thr; //初始化的時候右指針指向自己 if(!T)//如果樹為空那么初始化的時候指針也指向自己 Thr->lchild=Thr;else{Thr->lchild=T;//頭結點左孩子指向根,pre初始值為頭結點 InThreading(T);//對以T為根的二叉樹進行線索化 pre->rchild=Thr;//中序線索化后pre為遍歷的最后結點 ,讓最后的結點的右結點指向頭結點 pre->RTag=1;Thr->rchild=pre;//頭結點的右索引指向pre } }5.遍歷線索二叉樹
下圖中是一個按照中序遍歷建立的線索二叉樹。其中,實線表示指針,指向的是左孩子或者右孩子。虛線表示線索,指向的是該結點的直接前趨或者直接后繼。
??使用線索二叉樹時,會經常遇到一個問題,如上圖中,結點8的直接后繼直接通過指針域獲得,為結點5;而由于結點5的度為2 ,無法利用指針域指向后繼結點,整個鏈表斷掉了。當在遍歷過程,遇到這種問題是解決的辦法就是:尋找先序、中序、后序遍歷的規律,找到下一個結點。
???在先序遍歷過程中,如果結點因為有右孩子導致無法找到其后繼結點,如果結點有左孩子,則后繼結點是其左孩子;否則,就一定是右孩子。拿上圖舉例,結點2的后繼結點是其左孩子結點4 ,如果結點4不存在的話,就是結點5 。
??在中序遍歷過程中,結點的后繼是遍歷其右子樹時訪問的第一個結點,也就是右子樹中位于最左下的結點。例如上圖中結點5,后繼結點為結點11,是其右子樹中位于最左邊的結點。反之,結點的前趨是左子樹最后訪問的那個結點。
??后序遍歷中找后繼結點需要分為3 種情況:
??1. 如果該結點是二叉樹的根,后繼結點為空;
??2. 如果該結點是父結點的右孩子(或者是左孩子,但是父結點沒有右孩子),后繼結點是父結點;
??3. 如果該結點是父結點的左孩子,且父結點有右子樹,后繼結點為父結點的右子樹在后序遍歷列出的第一個結點。
??使用后序遍歷建立的線索二叉樹,在真正使用過程中遇到鏈表的斷點時,需要訪問父結點,所以在初步建立二叉樹時,宜采用三叉鏈表做存儲結構。
表弟:這么多,勸退?
我:我們重點掌握中序的就行,下面我實現一下中序線索二叉樹的遍歷的非遞歸代碼你看看
結合下面圖一起看,下面圖的中序遍歷結果時4 2 8 5 11 9 12 1 6 3 7
總結
以上是生活随笔為你收集整理的我这么讲线索二叉树,我三岁大的表弟笑了笑的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 循环队列之舞伴问题(含源码详解)
- 下一篇: 筛选法求素数