深入学习二叉树(二) 线索二叉树
深入學習二叉樹(二) 線索二叉樹
1 前言
在上一篇簡單二叉樹的學習中,初步介紹了二叉樹的一些基礎知識,本篇文章將重點介紹二叉樹的一種變形——線索二叉樹。
2 線索二叉樹
2.1 產生背景
現有一棵結點數目為n的二叉樹,采用二叉鏈表的形式存儲。對于每個結點均有指向左右孩子的兩個指針域,而結點為n的二叉樹一共有n-1條有效分支路徑。那么,則二叉鏈表中存在2n-(n-1)=n+1個空指針域。那么,這些空指針造成了空間浪費。
例如:圖2.1所示一棵二叉樹一共有10個結點,空指針^有11個。
此外,當對二叉樹進行中序遍歷時可以得到二叉樹的中序序列。例如:圖2.1所示二叉樹的中序遍歷結果為HDIBJEAFCG,可以得知A的前驅結點為E,后繼結點為F。但是,這種關系的獲得是建立在完成遍歷后得到的,那么可不可以在建立二叉樹時就記錄下前驅后繼的關系呢,那么在后續尋找前驅結點和后繼結點時將大大提升效率。
2.2 線索化
現將某結點的空指針域指向該結點的前驅后繼,定義規則如下:
若結點的左子樹為空,則該結點的左孩子指針指向其前驅結點。
若結點的右子樹為空,則該結點的右孩子指針指向其后繼結點。
這種指向前驅和后繼的指針稱為線索。將一棵普通二叉樹以某種次序遍歷,并添加線索的過程稱為線索化。
按照規則將圖2.1所示二叉樹線索化后如圖2.2所示:
圖中黑色點畫線為指向后繼的線索,紫色虛線為指向前驅的線索。
可以看出通過線索化,既解決了空間浪費問題,又解決了前驅后繼的記錄問題。
2.3 線索化帶來新問題
經過2.2節講解后,可以將一棵二叉樹線索化為一棵線索二叉樹,那么新的問題產生了。我們如何區分一個結點的lchild指針是指向左孩子還是前驅結點呢?例如:對于圖2.2所示的結點E,如何區分其lchild的指向的結點J是其左孩子還是前驅結點呢?
為了解決這一問題,現需要添加標志位ltag,rtag。并定義規則如下:
ltag為0時,指向左孩子,為1時指向前驅
rtag為0時,指向右孩子,為1時指向后繼
添加ltag和rtag屬性后的結點結構如下:
圖2.2所示線索二叉樹轉變為圖2.3所示的二叉樹。
2.4 線索二叉樹結點數據結構
//#define Link 0//指針標志 //#define Thread 1//線索標志 typedef char TElemType; //中序線索二叉樹 typedef enum PointerTag {Link, Thread};//結點的child域類型,link表示是指針,指向孩子結點,thread表示是線索,指示前驅或后繼結點 //定義結點數據結構 typedef struct ThrBiNode{ TElemType data; ThrBiNode *lchild, *rchild;//左右孩子指針 PointerTag lTag, rTag;//左右標志 }ThrBiNode, *ThrBiTree;2.5 中序遍歷建立線索二叉樹
中序遍歷的方法已經在第一篇二叉樹基礎中講解過,那么實現線索化的過程就是在中序遍歷同時修改結點空指針的指向。
采用中序遍歷的訪問順序實現一棵二叉樹的線索化過程代碼如下:
2.6 加上頭結點,遍歷線索二叉樹
加上線索的二叉樹結構是一個雙向鏈表結構,為了便于遍歷線索二叉樹,我們為其添加一個頭結點,頭結點左孩子指向原二叉樹的根結點,右孩子指針指向中序遍歷的最后一個結點。同時,將第一個結點左孩子指針指向頭結點,最后一個結點的右孩子指針指向頭結點。
圖2.3所示線索二叉樹添加頭結點后如圖2.4所示:
帶有頭結點的線索二叉樹遍歷代碼如下:
3 結語
線索二叉樹充分利用了指針空間,同時又便于尋找結點的前驅結點和后繼結點。線索二叉樹適用于經常需要遍歷尋找結點前驅或者后繼結點的二叉樹。
總結
以上是生活随笔為你收集整理的深入学习二叉树(二) 线索二叉树的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Scikit-Learn 机器学习笔记
- 下一篇: Scikit-Learn 机器学习笔记