深入學(xué)習(xí)二叉樹(二) 線索二叉樹


1 前言

在上一篇簡(jiǎn)單二叉樹的學(xué)習(xí)中爽彤,初步介紹了二叉樹的一些基礎(chǔ)知識(shí)昵济,本篇文章將重點(diǎn)介紹二叉樹的一種變形——線索二叉樹蕊连。

2 線索二叉樹

2.1 產(chǎn)生背景

現(xiàn)有一棵結(jié)點(diǎn)數(shù)目為n的二叉樹森缠,采用二叉鏈表的形式存儲(chǔ)深滚。對(duì)于每個(gè)結(jié)點(diǎn)均有指向左右孩子的兩個(gè)指針域奕谭,而結(jié)點(diǎn)為n的二叉樹一共有n-1條有效分支路徑涣觉。那么,則二叉鏈表中存在2n-(n-1)=n+1個(gè)空指針域血柳。那么官册,這些空指針造成了空間浪費(fèi)。
例如:圖2.1所示一棵二叉樹一共有10個(gè)結(jié)點(diǎn)难捌,空指針^有11個(gè)膝宁。


圖2.1

此外,當(dāng)對(duì)二叉樹進(jìn)行中序遍歷時(shí)可以得到二叉樹的中序序列根吁。例如:圖2.1所示二叉樹的中序遍歷結(jié)果為HDIBJEAFCG员淫,可以得知A的前驅(qū)結(jié)點(diǎn)為E,后繼結(jié)點(diǎn)為F婴栽。但是满粗,這種關(guān)系的獲得是建立在完成遍歷后得到的,那么可不可以在建立二叉樹時(shí)就記錄下前驅(qū)后繼的關(guān)系呢愚争,那么在后續(xù)尋找前驅(qū)結(jié)點(diǎn)和后繼結(jié)點(diǎn)時(shí)將大大提升效率映皆。

2.2 線索化

現(xiàn)將某結(jié)點(diǎn)的空指針域指向該結(jié)點(diǎn)的前驅(qū)后繼,定義規(guī)則如下:

若結(jié)點(diǎn)的左子樹為空轰枝,則該結(jié)點(diǎn)的左孩子指針指向其前驅(qū)結(jié)點(diǎn)捅彻。
若結(jié)點(diǎn)的右子樹為空,則該結(jié)點(diǎn)的右孩子指針指向其后繼結(jié)點(diǎn)鞍陨。

這種指向前驅(qū)和后繼的指針稱為線索步淹。將一棵普通二叉樹以某種次序遍歷,并添加線索的過程稱為線索化诚撵。
按照規(guī)則將圖2.1所示二叉樹線索化后如圖2.2所示:


圖2.2 線索二叉樹

圖中黑色點(diǎn)畫線為指向后繼的線索缭裆,紫色虛線為指向前驅(qū)的線索。
可以看出通過線索化寿烟,既解決了空間浪費(fèi)問題澈驼,又解決了前驅(qū)后繼的記錄問題。

2.3 線索化帶來新問題

經(jīng)過2.2節(jié)講解后筛武,可以將一棵二叉樹線索化為一棵線索二叉樹缝其,那么新的問題產(chǎn)生了。我們?nèi)绾螀^(qū)分一個(gè)結(jié)點(diǎn)的lchild指針是指向左孩子還是前驅(qū)結(jié)點(diǎn)呢徘六?例如:對(duì)于圖2.2所示的結(jié)點(diǎn)E内边,如何區(qū)分其lchild的指向的結(jié)點(diǎn)J是其左孩子還是前驅(qū)結(jié)點(diǎn)呢?
為了解決這一問題待锈,現(xiàn)需要添加標(biāo)志位ltag漠其,rtag。并定義規(guī)則如下:

ltag為0時(shí),指向左孩子辉懒,為1時(shí)指向前驅(qū)
rtag為0時(shí)阳惹,指向右孩子,為1時(shí)指向后繼

添加ltag和rtag屬性后的結(jié)點(diǎn)結(jié)構(gòu)如下:


添加標(biāo)志線索二叉樹結(jié)點(diǎn).png

圖2.2所示線索二叉樹轉(zhuǎn)變?yōu)閳D2.3所示的二叉樹眶俩。


圖2.3 添加標(biāo)志位的線索二叉樹

2.4 線索二叉樹結(jié)點(diǎn)數(shù)據(jù)結(jié)構(gòu)

//#define Link 0//指針標(biāo)志  
//#define Thread 1//線索標(biāo)志  
typedef char TElemType;   
//中序線索二叉樹  
typedef enum PointerTag {Link, Thread};//結(jié)點(diǎn)的child域類型莹汤,link表示是指針,指向孩子結(jié)點(diǎn)颠印,thread表示是線索纲岭,指示前驅(qū)或后繼結(jié)點(diǎn)  
//定義結(jié)點(diǎn)數(shù)據(jù)結(jié)構(gòu)
typedef struct ThrBiNode{  
    TElemType data;  
    ThrBiNode *lchild, *rchild;//左右孩子指針  
    PointerTag lTag, rTag;//左右標(biāo)志  
}ThrBiNode, *ThrBiTree;  

2.5 中序遍歷建立線索二叉樹

中序遍歷的方法已經(jīng)在第一篇二叉樹基礎(chǔ)中講解過,那么實(shí)現(xiàn)線索化的過程就是在中序遍歷同時(shí)修改結(jié)點(diǎn)空指針的指向线罕。
采用中序遍歷的訪問順序?qū)崿F(xiàn)一棵二叉樹的線索化過程代碼如下:

//中序遍歷進(jìn)行中序線索化
void inThreading(ThrBiTree T, ThrBiTree &pre){  
    if(T){  
        inThreading(T->lchild, pre);//左子樹線索化  
  
        if(!T->lchild){//當(dāng)前結(jié)點(diǎn)的左孩子為空  
            T->lTag = Thread;  
            T->lchild = pre;  
        }else{  
            T->lTag = Link;  
        }  
  
        if(!pre->rchild){//前驅(qū)結(jié)點(diǎn)的右孩子為空  
            pre->rTag = Thread;  
            pre->rchild = T;  
        }else{  
            pre->rTag = Link;  
        }  
        pre = T;          
        inThreading(T->rchild, pre);//右子樹線索化  
    }  
}  

2.6 加上頭結(jié)點(diǎn)止潮,遍歷線索二叉樹

加上線索的二叉樹結(jié)構(gòu)是一個(gè)雙向鏈表結(jié)構(gòu),為了便于遍歷線索二叉樹钞楼,我們?yōu)槠涮砑右粋€(gè)頭結(jié)點(diǎn)喇闸,頭結(jié)點(diǎn)左孩子指向原二叉樹的根結(jié)點(diǎn),右孩子指針指向中序遍歷的最后一個(gè)結(jié)點(diǎn)询件。同時(shí)燃乍,將第一個(gè)結(jié)點(diǎn)左孩子指針指向頭結(jié)點(diǎn),最后一個(gè)結(jié)點(diǎn)的右孩子指針指向頭結(jié)點(diǎn)宛琅。
圖2.3所示線索二叉樹添加頭結(jié)點(diǎn)后如圖2.4所示:


圖2.4 帶有頭結(jié)點(diǎn)的線索二叉樹

帶有頭結(jié)點(diǎn)的線索二叉樹遍歷代碼如下:

//T指向頭結(jié)點(diǎn)刻蟹,頭結(jié)點(diǎn)的lchild鏈域指針指向二叉樹的根結(jié)點(diǎn)  
//中序遍歷打印二叉線索樹T(非遞歸算法)  
void inOrderTraversePrint(ThrBiTree T){  
    ThrBiNode *p = T->lchild;//p指向根結(jié)點(diǎn)  
      
    while(p != T){//空樹或遍歷結(jié)束時(shí),p == T  
        while(p->lTag == Link){  
            p = p->lchild;  
        }  
        //此時(shí)p指向中序遍歷序列的第一個(gè)結(jié)點(diǎn)(最左下的結(jié)點(diǎn))  
  
        printf("%c ", p->data);//打雍俦佟(訪問)其左子樹為空的結(jié)點(diǎn)  
  
        while(p->rTag == Thread && p->rchild != T){  
            p = p->rchild;  
            printf("%c ", p->data);//訪問后繼結(jié)點(diǎn)  
        }  
        //當(dāng)p所指結(jié)點(diǎn)的rchild指向的是孩子結(jié)點(diǎn)而不是線索時(shí)舆瘪,p的后繼應(yīng)該是其右子樹的最左下的結(jié)點(diǎn),即遍歷其右子樹時(shí)訪問的第一個(gè)節(jié)點(diǎn)  
        p = p->rchild;  
    }  
    printf("\n");  
}  

3 結(jié)語

線索二叉樹充分利用了指針空間红伦,同時(shí)又便于尋找結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)和后繼結(jié)點(diǎn)英古。線索二叉樹適用于經(jīng)常需要遍歷尋找結(jié)點(diǎn)前驅(qū)或者后繼結(jié)點(diǎn)的二叉樹。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末昙读,一起剝皮案震驚了整個(gè)濱河市召调,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌箕戳,老刑警劉巖某残,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件国撵,死亡現(xiàn)場(chǎng)離奇詭異陵吸,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)介牙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門壮虫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事囚似∈B#” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵饶唤,是天一觀的道長(zhǎng)徐伐。 經(jīng)常有香客問我,道長(zhǎng)募狂,這世上最難降的妖魔是什么办素? 我笑而不...
    開封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮祸穷,結(jié)果婚禮上性穿,老公的妹妹穿的比我還像新娘。我一直安慰自己雷滚,他們只是感情好需曾,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著祈远,像睡著了一般呆万。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上绊含,一...
    開封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天桑嘶,我揣著相機(jī)與錄音,去河邊找鬼躬充。 笑死逃顶,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的充甚。 我是一名探鬼主播以政,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼伴找!你這毒婦竟也來了盈蛮?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤技矮,失蹤者是張志新(化名)和其女友劉穎抖誉,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體衰倦,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡袒炉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了樊零。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片我磁。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡孽文,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出夺艰,到底是詐尸還是另有隱情芋哭,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布郁副,位于F島的核電站减牺,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏存谎。R本人自食惡果不足惜烹植,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望愕贡。 院中可真熱鬧草雕,春花似錦、人聲如沸固以。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽憨琳。三九已至诫钓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間篙螟,已是汗流浹背菌湃。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留遍略,地道東北人惧所。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像绪杏,于是被迫代替她去往敵國(guó)和親下愈。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 四、樹與二叉樹 1. 二叉樹的順序存儲(chǔ)結(jié)構(gòu) 二叉樹的順序存儲(chǔ)就是用數(shù)組存儲(chǔ)二叉樹僧著。二叉樹的每個(gè)結(jié)點(diǎn)在順序存儲(chǔ)中都有...
    MinoyJet閱讀 1,512評(píng)論 0 7
  • 前言 樹是數(shù)據(jù)結(jié)構(gòu)中的重中之重履因,尤其以各類二叉樹為學(xué)習(xí)的難點(diǎn)。一直以來盹愚,對(duì)于樹的掌握都是模棱兩可的狀態(tài)栅迄,現(xiàn)在希望通...
    MrHorse1992閱讀 353,482評(píng)論 51 536
  • 一、 概念 二叉樹按照先序杯拐、中序霞篡、后續(xù)遍歷的方法形成一個(gè)線性序列后,每個(gè)結(jié)點(diǎn)(除第一個(gè)和最后一個(gè)外)端逼,都有且僅有一...
    Qi0907閱讀 1,431評(píng)論 0 1
  • 概念 樹是什么 樹(Tree)是n(n>=0)個(gè)結(jié)點(diǎn)的有限集朗兵。 n = 0的樹是空樹。 在任意一棵非空樹中: 有且...
    剛剛悟道閱讀 5,034評(píng)論 1 16
  • 原鏈接:理解線索二叉樹|CloudWong 線索二叉樹原理 遍歷二叉樹的其實(shí)就是以一定規(guī)則將二叉樹中的結(jié)點(diǎn)排列成一...
    簡(jiǎn)Cloud閱讀 8,380評(píng)論 1 16