版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
⑥樹的概念
胤<:::x概念
⑥二叉樹
胤二叉樹的定義
胤二叉樹的性質(zhì)
置二叉樹的存儲結(jié)構(gòu)
⑥二叉樹的遍歷
胤二叉樹的遍歷
⑥線索二叉樹
胤線索二叉樹
蜀樹和森林
胤樹、森林和二叉樹的轉(zhuǎn)
胤樹的存儲結(jié)構(gòu)
.樹和森林的遍歷
⑥哈夫曼樹及其應(yīng)用
胤最優(yōu)二叉樹
胤哈夫曼編碼
樹的概念
本章簡介
樹形結(jié)構(gòu)是一類重要的非線性結(jié)構(gòu)。樹形結(jié)構(gòu)是結(jié)點(diǎn)之間有分支,并具有層次關(guān)系的結(jié)構(gòu)。
它非常類似于自然界中的樹。
樹結(jié)構(gòu)在客觀世界中是大量存在的,例如家譜、行政組織機(jī)構(gòu)都可用樹形象地表示。
樹在計(jì)算機(jī)領(lǐng)域中也有著廣泛的應(yīng)用,例如在編譯程序中,用樹來表示源程序的語法結(jié)構(gòu);
在數(shù)據(jù)庫系統(tǒng)中,可用樹來組織信息;在分析算法的行為時(shí),可用樹來描述其執(zhí)行過程。
本章重點(diǎn)討論二叉樹的存儲表示及其各種運(yùn)算,并研究一般樹和森林與二叉樹的轉(zhuǎn)換關(guān)系,
最后介紹樹的應(yīng)用實(shí)例。
樹的概念
1.家族樹
在現(xiàn)實(shí)生活中,有入如下血統(tǒng)關(guān)系的家族可用樹形圖表示:
張?jiān)从腥齻€(gè)孩子張明、張亮和張麗;
張明有兩個(gè)孩子張林和張維;
張亮有三個(gè)孩子張平、張華和張群;
張平有兩個(gè)孩子張晶和張磊。
張?jiān)?/p>
以上表示很像一棵倒畫的樹。其中"樹根"是張?jiān)矗瑯涞?分支點(diǎn)"是張明、張亮和張平,該家族
的其余成員均是"樹葉",而樹枝(即圖中的線段)則描述了家族成員之間的關(guān)系。顯然,以張?jiān)礊?/p>
根的樹是一個(gè)大家庭。它可以分成張明、張亮和張麗為根的三個(gè)小家庭;每個(gè)小家庭又都是一個(gè)
樹形結(jié)構(gòu)。
2.樹的定義
樹的遞歸定義:
樹(Tree)是n(應(yīng)0)個(gè)結(jié)點(diǎn)的有限集T,T為空時(shí)稱為空樹,否則它滿足如下兩個(gè)條件:
(1)有且僅有個(gè)特定的稱為根(Root)的結(jié)點(diǎn):
(2)其余的結(jié)點(diǎn)可分為m(mZO)個(gè)互不相交的子集TI,T2,....Tm,其中每個(gè)子集本身又是一棵樹,
并稱其為根的子樹(Subree)。
注意:
樹的遞歸定義刻畫了樹的固有特性:一棵非空樹是由若干棵子樹構(gòu)成的,而子樹又可由若
干棵更小的子樹構(gòu)成。
上一頁下一頁
3.樹的表示
(1)樹形圖表示
樹形圖表示是樹結(jié)構(gòu)的主要表示方法。
樹的樹形圖表示中:結(jié)點(diǎn)用圓圈表示,結(jié)點(diǎn)的名字寫在圓圈旁邊(有時(shí)亦可寫在圓圈內(nèi))。
如〉樹形表示法00嵌套集合表示法(c)凹入表表示法
(A(B(E,F(I,.D),C,D(G,H)))
(d)廣義表表示法
樹的各種表示法
用該定義來分析上圖(a)所示的樹:
圖中的樹由結(jié)點(diǎn)的有限集T={A,B,C,D,E,F,C,H,I,J}所構(gòu)成,其中A是根結(jié)點(diǎn),T
中其余結(jié)點(diǎn)可分成三個(gè)互不相交的子集:
T產(chǎn){B,E,F,I,J},
T2={C},
T3={D,G,H}。
「、T2和T3是根A的三棵子樹、且本身又都是一棵樹。例如T”其根為B,其余結(jié)點(diǎn)可分為
兩個(gè)互不相交的的子集T“={E}和TD={F,I,J},它們都是B的子樹。顯然T”是只含一個(gè)根結(jié)
點(diǎn)E的樹,而Tn的根F又有兩棵互不相交的子樹⑴和{J},其本身又都是只含一個(gè)根結(jié)點(diǎn)的樹。
(2)樹的其他表示法
①嵌套集合表示法
是用集合的包含關(guān)系來描述樹結(jié)構(gòu)。
上圖(a)樹的嵌套集合表示法如圖(b)
②凹入表表示法
類似于書的目錄,上圖(a)樹的網(wǎng)入表示法如圖(c)
③廣義表表示法
用廣義表的形式表示的。上圖(a)樹的廣義表表示法如圖(d)
(A(B(E,F(I,J)),C,D(G,H)))
4.樹結(jié)構(gòu)的基本術(shù)語
(1)結(jié)點(diǎn)的度(Degree)
樹中的?個(gè)結(jié)點(diǎn)擁有的子樹數(shù)稱為該結(jié)點(diǎn)的度(Degree)。
?棵樹的度是指該樹中結(jié)點(diǎn)的最大度數(shù)。
度為零的結(jié)點(diǎn)稱為葉子(Leaf)或終端結(jié)點(diǎn)o
度不為零的結(jié)點(diǎn)稱分支結(jié)點(diǎn)或非終端結(jié)點(diǎn)。
除根結(jié)點(diǎn)之外的分支結(jié)點(diǎn)統(tǒng)稱為內(nèi)部結(jié)點(diǎn)。
根結(jié)點(diǎn)又稱為開始結(jié)點(diǎn)。
(2)孩子(ChiId)和雙親(Parents)
樹中某個(gè)結(jié)點(diǎn)的子樹之根稱為該結(jié)點(diǎn)的孩子(Child)或兒子,相應(yīng)地,該結(jié)點(diǎn)稱為孩子的
雙親(Parents)或父親。
同一個(gè)雙親的孩子稱為兄弟(Sibling)。
(3)祖先(Ancestor)和子孫(Descendant)
①路徑(path)
若樹中存在一個(gè)結(jié)點(diǎn)序列k”k”…,k,,使得上是1<1的雙親(lWi〈j),則稱該結(jié)點(diǎn)序
列是從L到k,的一條路徑(Path)或道路。
路徑的長度指路徑所經(jīng)過的邊(即連接兩個(gè)結(jié)點(diǎn)的線段)的數(shù)目,等于j-k
注意:
若一個(gè)結(jié)點(diǎn)序列是路徑,則在樹的樹形圖表示中,該結(jié)點(diǎn)序列"自上而下''地通過路徑上的
每條邊。
從樹的根結(jié)點(diǎn)到樹中其余結(jié)點(diǎn)均存在一條惟一的路徑。
②祖先(Ancestor)和子孫(Descendant)
若樹中結(jié)點(diǎn)k到k,存在一條路徑,則稱k是X的祖先(Ancestor),k是k的子孫
(Descendant)?
一個(gè)結(jié)點(diǎn)的祖先是從根結(jié)點(diǎn)到該結(jié)點(diǎn)路徑上所經(jīng)過的所有結(jié)點(diǎn),而一個(gè)結(jié)點(diǎn)的子孫則是以
該結(jié)點(diǎn)為根的子樹中的所有結(jié)點(diǎn)。
約定:
結(jié)點(diǎn)k的祖先和子孫不包含結(jié)點(diǎn)k本身。
(4)結(jié)點(diǎn)的層數(shù)(Level)和樹的高度(Height)
結(jié)點(diǎn)的層數(shù)(Level)從根起算:
根的層數(shù)為1
其余結(jié)點(diǎn)的層數(shù)等于其雙親結(jié)點(diǎn)的層數(shù)加1。
雙親在同一層的結(jié)點(diǎn)互為堂兄弟。
樹中結(jié)點(diǎn)的最大層數(shù)稱為樹的高度(Height)或深度(Depth)。
注意,
很多文獻(xiàn)中將樹根的層數(shù)定義為0。
(5)有序樹(OrderedTree)和無序樹(UnoderedTree)
若將樹中每個(gè)結(jié)點(diǎn)的各子樹看成是從左到右有次序的(即不能互換),則稱該樹為有序樹
(OrderedTree);否則稱為無序樹(UnoderedTree)?
注意:
若不特別指明,?般討論的樹都是有序樹。
(6)森林(Forest)
森林(Forest)是m(m20)棵立不相交的樹的集合。
樹和森林的概念相近。刪去一棵樹的根,就得到?個(gè)森林;反之,加上?個(gè)結(jié)點(diǎn)作樹根,
森林就變?yōu)橐豢脴洹?/p>
5.樹形結(jié)構(gòu)的邏輯特征
樹形結(jié)構(gòu)的邏輯特征可用樹中結(jié)點(diǎn)之間的父子關(guān)系來描述:
(1)樹中任一結(jié)點(diǎn)都可以有零個(gè)或多個(gè)直接后繼(即孩子)結(jié)點(diǎn),但至多只能有一個(gè)直接前趨(即
雙親)結(jié)點(diǎn)。
(2)樹中只有根結(jié)點(diǎn)無前趨,它是開始結(jié)點(diǎn);葉結(jié)點(diǎn)無后繼,它們是終端結(jié)點(diǎn)。
(3)祖先與子孫的關(guān)系是對父子關(guān)系的延拓,它定義了樹中結(jié)點(diǎn)之間的縱向次序。
(4)有序樹中,同一組兄弟結(jié)點(diǎn)從左到右有長幼之分。
對這一關(guān)系加以延拓,規(guī)定若總和上是兄弟,且心在L的左邊,則笛的任一子孫都在L
的任孫的左邊,那么就定義了樹中結(jié)點(diǎn)之間的橫向次序。
二叉樹
二叉樹是樹形結(jié)構(gòu)的一個(gè)重要類型。許多實(shí)際問題抽象出來的數(shù)據(jù)結(jié)構(gòu)往往是二叉樹的形
式,即使是?般的樹也能簡單地轉(zhuǎn)換為二叉樹,而且二叉樹的存儲結(jié)構(gòu)及其算法都較為簡單,因
此二叉樹顯得特別重要。
1.二叉樹的遞歸定義
二叉樹(BinaryTree)是n(n》O)個(gè)結(jié)點(diǎn)的有限集,它或者是空集(n=0),或者由一個(gè)根結(jié)
點(diǎn)及兩棵互不相交的、分別稱作這個(gè)根的左子樹和右子樹的二叉樹組成。
2.二叉樹的五種基本形態(tài)
二叉樹可以是空集;根可以有空的左廣樹或右子樹;或者左、右子樹皆為空。
二叉樹的五種基本形態(tài)如下圖所示。
I)空二義樹<b)僅有一個(gè)根結(jié)點(diǎn)的二叉樹(c)右子樹為空的.叉樹
(d)左子樹為空的:叉樹(e)左右子樹均非空的:叉樹
:義樹的萬種基本形態(tài)
3.二叉樹不是樹的特例
(1)二叉樹與無序樹不同
二叉樹中,每個(gè)結(jié)點(diǎn)最多只能有兩棵子樹,并且有左右之分。
二義樹并非是樹的特殊情形,它們是兩種不同的數(shù)據(jù)結(jié)構(gòu)。
(2)二叉樹與度數(shù)為2的有序樹不同
在有序樹中,雖然?個(gè)結(jié)點(diǎn)的孩子之間是有左右次序的,但是若該結(jié)點(diǎn)只有?個(gè)孩子,就無
須區(qū)分其左右次序。而在二叉樹中,即使是??個(gè)孩子也有左右之分。
【例】下圖中(a)和(b)是兩棵不同的二叉樹,它們同右圖中的普通樹(作為有序樹或無序樹)
很相似,但卻不等同于這棵普通樹。若將這三棵樹均看做普通樹,則它們就是相同的了。
AAOA
二叉樹并非是樹的特殊情形,它們是兩種不同的數(shù)據(jù)結(jié)構(gòu)。
二叉樹具有以下重要性質(zhì):
性質(zhì)1二叉樹第i層上的結(jié)點(diǎn)數(shù)目最多為2r(之1)。
證明:用數(shù)學(xué)歸納法證明:
歸納基礎(chǔ):i=l時(shí),有2?.2°=1。因?yàn)榈?層上只有一個(gè)根結(jié)點(diǎn),所以命題成立。
歸納假設(shè):假設(shè)對所有的j(lWj<i)命題成立,即第j層上至多有2打個(gè)結(jié)點(diǎn),證明j=i時(shí)命題
亦成立。
歸納步驟:根據(jù)歸納假設(shè),第i-1層上至多有2'2個(gè)結(jié)點(diǎn)。由于二叉樹的每個(gè)結(jié)點(diǎn)至多有兩
個(gè)孩子,故第i層上的結(jié)點(diǎn)數(shù)至多是第i-1層上的最大結(jié)點(diǎn)數(shù)的2倍。即j=i時(shí),該層上至多有
2、2“2=2*個(gè)結(jié)點(diǎn),故命題成立。
性質(zhì)2深度為k的二叉樹至多有2k-l個(gè)結(jié)點(diǎn)(kR)。
W:在具有相同深度的二叉樹中,僅當(dāng)每一層都含有最大結(jié)點(diǎn)數(shù)時(shí),其樹中結(jié)點(diǎn)數(shù)最多。因此
利用性質(zhì)1可得,深度為k的二叉樹的結(jié)點(diǎn)數(shù)至多為:
2°+2l+...+2k-|=2k-l
故命題正確。
性質(zhì)3在任意-棵二叉樹中,若終端結(jié)點(diǎn)的個(gè)數(shù)為n0,度為2的結(jié)點(diǎn)數(shù)為叫,則%=%+1。
證明:因?yàn)槎鏄渲兴薪Y(jié)點(diǎn)的度數(shù)均不大于2,所以結(jié)點(diǎn)總數(shù)(記為n)應(yīng)等于0度結(jié)點(diǎn)數(shù)、1
度結(jié)點(diǎn)(記為由)和2度結(jié)點(diǎn)數(shù)之和:
n=n0+ri|+n2(式子1)
另一方面,1度結(jié)點(diǎn)有一個(gè)孩子,2度結(jié)點(diǎn)有兩個(gè)孩子,故二叉樹中孩子結(jié)點(diǎn)總數(shù)是:
ri|+2n2
樹中只有根結(jié)點(diǎn)不是任何結(jié)點(diǎn)的孩子,故二叉樹中的結(jié)點(diǎn)總數(shù)又可表示為:
n=n1+2n2+1(式子2)
由式子1和式子2得到:
no=n2+l
滿二叉樹和完全二叉樹是二叉樹的兩種特殊情形。
1、滿二叉樹(FullBinaryTree)
一棵深度為k且有2k-l個(gè)結(jié)點(diǎn)的二又樹稱為滿二叉樹。
滿二叉樹的特點(diǎn):
(1)每一層上的結(jié)點(diǎn)數(shù)都達(dá)到最大值。即對給定的高度,它是具有最多結(jié)點(diǎn)數(shù)的二叉樹。
(2)滿二叉樹中不存在度數(shù)為1的結(jié)點(diǎn),每個(gè)分支結(jié)點(diǎn)均有兩棵高度相同的子樹,且樹葉
都在最下一層上。
【例】圖(a)是一個(gè)深度為4的滿二叉樹。
(a)滿二又樹(b)完全:叉樹(c)非完全.叉樹
特殊形態(tài)的二叉樹
2、完全二叉樹(CompleteBinaryTree)
若一棵二叉樹至多只有最下面的兩層上結(jié)點(diǎn)的度數(shù)可以小于2,并且最下一層上的結(jié)點(diǎn)都集中
在該層最左邊的若干位置上,則此二又樹稱為完全二叉樹。
特點(diǎn):
(1)滿二叉樹是完全二叉樹,完全二叉樹不一定是滿二叉樹。
(2)在滿二叉樹的最下一層上,從最右邊開始連續(xù)刪去若干結(jié)點(diǎn)后得到的二叉樹仍然是一棵
完全二叉樹。
(3)在完全二叉樹中,若某個(gè)結(jié)點(diǎn)沒有左孩子,則它一定沒有右孩子,即該結(jié)點(diǎn)必是葉結(jié)點(diǎn)。
【例】如圖(c)中,結(jié)點(diǎn)F沒有左孩子而有右孩子L,故它不是一棵完全二叉樹。
【例】圖(b)是一棵完全二叉樹。
性質(zhì)4具有n個(gè)結(jié)點(diǎn)的完全二叉樹的深度為
證明:設(shè)所求完全二叉樹的深度為k。由完全二叉樹定義可得:
深度為k得完全二叉樹的前k-1層是深度為k-1的滿二叉樹,-共有2kLi個(gè)結(jié)點(diǎn)。
由于完全二叉樹深度為k,故第k層上還有若干個(gè)結(jié)點(diǎn),因此該完全二叉樹的結(jié)點(diǎn)個(gè)數(shù):
n>2k-1-lo
另一方面,由性質(zhì)2可得:
n<2k-l,
即:2k-'-l<n<2k-l
由此可推出:2"wn<2k,取對數(shù)后有:
k-l<lgn<k
又因k-1和k是相鄰的兩個(gè)整數(shù),故有
A-1-LlgNj,
由此即得:
注意:
的證明【參見參考書目】
順序存儲結(jié)構(gòu)
該方法是把二叉樹的所有結(jié)點(diǎn)按照一定的線性次序存儲到一片連續(xù)的存儲單元中。結(jié)點(diǎn)在這
個(gè)序列中的相互位置還能反映出結(jié)點(diǎn)之間的邏輯關(guān)系。
1.完全二叉樹結(jié)點(diǎn)編號
(1)編號辦法
在一棵n個(gè)結(jié)點(diǎn)的完全二叉樹中,從樹根起,自上層到下層,每層從左至右,給所有結(jié)點(diǎn)
編號,能得到一個(gè)反映整個(gè)二叉樹結(jié)構(gòu)的線性序列。
【例】如下圖所示。
(2)編號特點(diǎn)
完全二叉樹中除最下面一層外,各層都充滿了結(jié)點(diǎn)。每一層的結(jié)點(diǎn)個(gè)數(shù)恰好是上一層結(jié)點(diǎn)
個(gè)數(shù)的2倍。從一個(gè)結(jié)點(diǎn)的編號就可推得其雙親,左、右孩子,兄弟等結(jié)點(diǎn)的編號。假設(shè)編號為
i的結(jié)點(diǎn)是k(lWiWn),則有:
①若i>l,則%的雙親編號為|f/2j;若i=l,則K,是根結(jié)點(diǎn),無雙親。
②若2iWn,則K,的左孩子的編號是2i:否則,K;無左孩子,即長必定是葉子。因此完全
二又樹中編號的結(jié)點(diǎn)必定是葉結(jié)點(diǎn)。
③若2i+lWn,則K,的右孩子的編號是2i+l;否則,K,無右孩子。
④若i為奇數(shù)且不為1,則K,的左兄弟的編號是i-1;否則,K:無左兄弟。
⑤若i為偶數(shù)且小于n,則K;的右兄弟的編號是i+1;否則,K,無右兄弟。
2.完全二叉樹的順序存儲
將完全二叉樹中所有結(jié)點(diǎn)按編號順序依次存儲在一個(gè)向量bt[O..n]中。
其中:
bt[l..n]用來存儲結(jié)點(diǎn)
bt[O]不用或用來存儲結(jié)點(diǎn)數(shù)目。
【例】表6.1是圖6.8所示的完全二叉樹的順序存儲結(jié)構(gòu),bt[O]為結(jié)點(diǎn)數(shù)目,b[7]的雙親、
左右孩子分別是bt[3]、bt[14]和bt[15]。
下標(biāo)01234567891011121314151617
17ABCDEFGIIIJKLMN0PQ
插入表6.1和圖6.8所不完全一義樹的順序存儲結(jié)構(gòu)
3.一般二叉樹的順序存儲
(1)具體方法
①將一般二叉樹添上一些“虛結(jié)點(diǎn)",成為"完全二叉樹”
②為了用結(jié)點(diǎn)在向量中的相對位置來表示結(jié)點(diǎn)之間的邏輯關(guān)系,按完全二叉樹形式給結(jié)點(diǎn)
編號
③將結(jié)點(diǎn)按編號存入向量對應(yīng)分量,其中"虛結(jié)點(diǎn)"用"U'表示
【例】圖6-9中單支樹的順序存儲結(jié)構(gòu)如卜,圖
下標(biāo),0「,234567
bt3A6B(t)@eC
圖6-9中單支樹的順序存儲結(jié)構(gòu)
(2)優(yōu)點(diǎn)和缺點(diǎn)
①對完全二叉樹而言,順序存儲結(jié)構(gòu)既簡單又節(jié)省存儲空間。
②一般的二叉樹采用順序存儲結(jié)構(gòu)時(shí),雖然簡單,但易造成存儲空間的浪費(fèi)。
【例】最壞的情況下,一個(gè)深度為k且只有k個(gè)結(jié)點(diǎn)的右單支樹需要2k-l個(gè)結(jié)點(diǎn)的存儲
空間。
③在對順序存儲的二叉樹做插入和刪除結(jié)點(diǎn)操作時(shí),要大量移動(dòng)結(jié)點(diǎn)。
4.二叉樹的順序存儲結(jié)構(gòu)類型定義
【參見參考書】
鏈?zhǔn)酱鎯Y(jié)構(gòu)
1.結(jié)點(diǎn)的結(jié)構(gòu)
二叉樹的每個(gè)結(jié)點(diǎn)最多有兩個(gè)孩子。用鏈接方式存儲二叉樹時(shí),每個(gè)結(jié)點(diǎn)除了存儲結(jié)點(diǎn)本
身的數(shù)據(jù)外,還應(yīng)設(shè)置兩個(gè)指針域Ichild和rchild,分別指向該結(jié)點(diǎn)的左孩子和右孩子。結(jié)點(diǎn)
的結(jié)構(gòu)為:
IchiIddatarchiId
2.結(jié)點(diǎn)的類型說明
typedefcharDataType;〃用了1可根據(jù)具體應(yīng)用定義DataType的實(shí)際類型
typedefstructnode{
DataTypedata;
Structnode*lchild,*rchild;〃左右孩子指針
}BinTNode;〃結(jié)點(diǎn)類型
typedefBinTNode*BinTree;〃BinTree為指向BinTNode類型結(jié)點(diǎn)的指針類型
3.二叉鏈表(二叉樹的常用鏈?zhǔn)酱鎯Y(jié)構(gòu))
在一棵二叉樹中,所有類型為BinTNode的結(jié)點(diǎn),再加上一個(gè)指向開始結(jié)點(diǎn)(即根結(jié)點(diǎn))的
BinTree型頭指針(即根指針)root,就構(gòu)成了二叉樹的鏈?zhǔn)酱鎯Y(jié)構(gòu),并將其稱為二叉鏈表。
【例】下面左圖所示二叉樹的二叉鏈表如下面中圖所示。
注意:
①一個(gè)二叉鏈表由根指針root惟一確定。若二叉樹為空,則root=NULL;若結(jié)點(diǎn)的某個(gè)
孩子不存在,則相應(yīng)的指針為空。
②具有n個(gè)結(jié)點(diǎn)的二叉鏈表中,共有2n個(gè)指針域。其中只有n-1個(gè)用來指示結(jié)點(diǎn)的左、
右孩子,其余的n+1個(gè)指針域?yàn)榭铡?/p>
4.帶雙親指針的二叉鏈表
經(jīng)常要在二叉樹中尋找某結(jié)點(diǎn)的雙親時(shí),可在每個(gè)結(jié)點(diǎn)上再加一個(gè)指向其雙親的指針
parent,形成--個(gè)帶雙親指針的二叉鏈表。
【例】上面右圖是上面左圖所示的二叉樹的帶雙親指針的二叉鏈表。
注意:
二叉樹存儲方法的選擇,主要依賴于所要實(shí)施的各種運(yùn)算的頻度。
二叉樹的遍歷
遍歷概念
所謂遍歷(Traversal)是指沿著某條搜索路線,依次對樹中每個(gè)結(jié)點(diǎn)均做一次且僅做一次
訪問。訪問結(jié)點(diǎn)所做的操作依賴于具體的應(yīng)用問題。
遍歷是二叉樹上最重要的運(yùn)算之一,是二叉樹上進(jìn)行其它運(yùn)算之基礎(chǔ)。
遍歷方案
1.遍歷方案
從二叉樹的遞歸定義可知,一棵非空的二叉樹由根結(jié)點(diǎn)及左、右子樹這三個(gè)基本部分組成。
因此,在任一給定結(jié)點(diǎn)上,可以按某種次序執(zhí)行三個(gè)操作:
(1)訪問結(jié)點(diǎn)本身(N),
(2)遍歷該結(jié)點(diǎn)的左子樹(L),
(3)遍歷該結(jié)點(diǎn)的右子樹(R)。
以上三種操作有六種執(zhí)行次序:
NLR、LNR,LRN、NRL、RNL、RLR
注意:
前三種次序與后三種次序?qū)ΨQ,故只討論先左后右的前三種次序。
2.三種遍歷的命名
根據(jù)訪問結(jié)點(diǎn)操作發(fā)生位置命名:
①NLR:前序遍歷(PreorderTraversal亦稱(先序遍歷))
——訪問結(jié)點(diǎn)的操作發(fā)生在遍歷其左右子樹之前。
②LNR:中序遍歷(InorderTraversal)
——訪問結(jié)點(diǎn)的操作發(fā)生在遍歷其左右子樹之中(間)。
③LRN:后序遍歷(PostorderTraversal)
——訪問結(jié)點(diǎn)的操作發(fā)生在遍歷其左右子樹之后。
注意:
由于被訪問的結(jié)點(diǎn)必是某子樹的根,所以N(Node)、L(Leftsubtlee)和R(Rightsubtree)
又可解釋為根、根的左子樹和根的右子樹。NLR、LNR和LRN分別又稱為先根遍歷、中根遍歷和
后根遍歷。
遍歷算法
1.中序遍歷的遞歸算法定義:
若二叉樹非空,則依次執(zhí)行如下操作:
(1)遍歷左子樹;
(2)訪問根結(jié)點(diǎn);
(3)遍歷右子樹。
2.先序遍歷的遞歸算法定義:
若二叉樹非空,則依次執(zhí)行如下操作:
(1)訪問根結(jié)點(diǎn);
(2)遍歷左子樹;
(3)遍歷右子樹。
3.后序遍歷得遞歸算法定義:
若二叉樹非空,則依次執(zhí)行如下操作:
(D遍歷左子樹;
(2)遍歷右子樹;
(3)訪問根結(jié)點(diǎn)。
4.中序遍歷的算法實(shí)現(xiàn)
用二叉鏈表做為存儲結(jié)構(gòu),中序遍歷算法可描述為:
voidInOrder(BinTreeT)
{〃算法里①~⑥是為了說明執(zhí)行過程加入的標(biāo)號
①if(T){//如果二叉樹非空
②InOrder(T->lchiId);
③printf(〃%c〃,T->data);//訪問結(jié)點(diǎn)
(4)InOrder(T->rchiId);
⑤}
⑥}//InOrder
遍歷序列
1.遍歷二叉樹的執(zhí)行蹤跡
三種遞歸遍歷算法的搜索路線相同(如下圖虛線所示)。
具體線路為:
從根結(jié)點(diǎn)出發(fā),逆時(shí)針沿著二叉樹外緣移動(dòng),對每個(gè)結(jié)點(diǎn)均途徑三次,最后回到根結(jié)點(diǎn)。
遍歷二叉樹的搜索路線
2.遍歷序列
(1)中序序列
中序遍歷二叉樹時(shí),對結(jié)點(diǎn)的訪問次序?yàn)橹行蛐蛄?/p>
【例】中序遍歷上圖所示的二叉樹時(shí),得到的中序序列為:
DBAECF
(2)先序序列
先序遍歷二叉樹時(shí),對結(jié)點(diǎn)的訪問次序?yàn)橄刃蛐蛄?/p>
【例】先序遍歷上圖所示的二叉樹時(shí),得到的先序序列為:
ABDCEF
(3)后序序列
后序遍歷二叉樹時(shí),對結(jié)點(diǎn)的訪問次序?yàn)楹笮蛐蛄?/p>
【例】后序遍歷上圖所示的二叉樹時(shí),得到的后序序列為:
DBEFCA
注意:
(1)在搜索路線中,若訪問結(jié)點(diǎn)均是第一次經(jīng)過結(jié)點(diǎn)時(shí)進(jìn)行的,則是前序遍歷;若訪問結(jié)
點(diǎn)均是在第二次(或第三次)經(jīng)過結(jié)點(diǎn)時(shí)進(jìn)行的,則是中序遍歷(或后序遍歷)。只要將搜索路線上
所有在第次、第二次和第三次經(jīng)過的結(jié)點(diǎn)分別列表,即可分別得到該二叉樹的前序序列、中序
序列和后序序列。
(2)上述三種序列都是線性序列,有且僅有一個(gè)開始結(jié)點(diǎn)和一個(gè)終端結(jié)點(diǎn),其余結(jié)點(diǎn)都有
且僅有一個(gè)前趨結(jié)點(diǎn)和一個(gè)后繼結(jié)點(diǎn)。為了區(qū)別于樹形結(jié)構(gòu)中前趨(即雙親)結(jié)點(diǎn)和后繼(即孩子)
結(jié)點(diǎn)的概念,對上述三種線性序列,要在某結(jié)點(diǎn)的前趨和后繼之前冠以其遍歷次序名稱。
【例】上圖所示的二叉樹中結(jié)點(diǎn)C,其前序前趨結(jié)點(diǎn)是D,前序后繼結(jié)點(diǎn)是E;中序前趨結(jié)點(diǎn)是E,
中序后繼結(jié)點(diǎn)是F:后序前趨結(jié)點(diǎn)是F,后序后繼結(jié)點(diǎn)是Ao但是就該樹的邏輯結(jié)構(gòu)而言,C的前
趨結(jié)點(diǎn)是A,后繼結(jié)點(diǎn)是E和F。
二叉鏈表的構(gòu)造
1.基本思想
基于先序遍歷的構(gòu)造,即以二叉樹的先序序列為輸入構(gòu)造。
注意:
先序序列中必須加入虛結(jié)點(diǎn)以示空指針的位置。
【例】
建立上圖所示二叉樹,其輸入的先序序列是:ABDS
2.構(gòu)造算法
假設(shè)虛結(jié)點(diǎn)輸入時(shí)以空格字符表示,相應(yīng)的構(gòu)造算法為:
voidCreateBinTree(BinTree*T)
{〃構(gòu)造二叉鏈表。T是指向根指針的指針,故修改*T就修改了實(shí)參(根指針)本身
charch;
if((ch=getchar())=='')*T=NULL;〃讀人空格,將相應(yīng)指針置空
else{〃讀人非空格
*T=(BinTNode*)malloc(sizeof(BinTNode));〃生成結(jié)點(diǎn)
(*T)->data=ch;
CreateBinTree(&(*T)->1child);〃構(gòu)造左子樹
CreateBinTree(&(*T)->rchiId);〃構(gòu)造右子樹
)
}
注意:
調(diào)用該算法時(shí),應(yīng)將待建立的二叉鏈表的根指針的地址作為實(shí)參。
【例】
設(shè)root是一根指針(即它的類型是BinTree),則調(diào)用CreateBinTree(&root)后root就指向了
已構(gòu)造好的二叉鏈表的根結(jié)點(diǎn)。
構(gòu)造二叉鏈表的其他方法【參見參考書目】
二叉樹建立過程見【動(dòng)畫演示】
二叉樹的應(yīng)用
【參見練習(xí)】
線索二叉樹
線索二叉樹概念
1.定義
n個(gè)結(jié)點(diǎn)的二叉鏈表中含有n+1個(gè)空指針域。利用二叉鏈表中的空指針域,存放指向結(jié)點(diǎn)
在某種遍歷次序下的前趨和后繼結(jié)點(diǎn)的指針(這種附加的指針稱為"線索。
這種加上了線索的二叉鏈表稱為線索鏈表,相應(yīng)的二叉樹稱為線索二叉樹
(ThreadedBinaryTree)?根據(jù)線索性質(zhì)的不同,線索二叉樹可分為前序線索二叉樹、中序線
索二叉樹和后序線索二叉樹三種。
注意:
線索鏈表解決了二叉鏈表找左、右孩子困難的問題,出現(xiàn)了無法直接找到該結(jié)點(diǎn)在某種遍
歷序列中的前趨和后繼結(jié)點(diǎn)的問題。
2.線索鏈表的結(jié)點(diǎn)結(jié)構(gòu)
線索鏈表中的結(jié)點(diǎn)結(jié)構(gòu)為:
IchildItagdatartagrchiId
其中:
Itag和rtag是增加的兩個(gè)標(biāo)志域,用來區(qū)分結(jié)點(diǎn)的左、右指針域是指向其左、右孩子的
指針,還是指向其前趨或后繼的線索。
句U靖點(diǎn)的前甫的左!MT
田UzAtM3叫"右SM
3.線索二叉樹的表示
【例】下面(a)圖所示的中序線索二叉樹,其線索鏈表如下面(b)圖所示。
(a)中序線索:又樹
(h)中序線索鏈表
中序線索:叉樹及其存儲結(jié)構(gòu)
注意:
圖中的實(shí)線表示指針,虛線表示線索。
結(jié)點(diǎn)C的左線索為空,表示C是中序序列的開始結(jié)點(diǎn),無前趨;
結(jié)點(diǎn)E的右線索為空,表示E是中序序列的終端結(jié)點(diǎn),無后繼。
線索二叉樹中,?個(gè)結(jié)點(diǎn)是葉結(jié)點(diǎn)的充要條件為:左、右標(biāo)志均是1。
二叉樹的線索化
1.線索化和線索化實(shí)質(zhì)
將二叉樹變?yōu)榫€索二叉樹的過程稱為線索化。
按某種次序?qū)⒍鏄渚€索化的實(shí)質(zhì)是:按該次序遍歷二叉樹,在遍歷過程中用線索取代空
指針。
具體過程可【參見動(dòng)畫演示】。
2.二叉樹的中序線索化
(1)分析
算法與中序遍歷算法類似。只需要將遍歷算法中訪問結(jié)點(diǎn)的操作具體化為建立正在訪問的
結(jié)點(diǎn)與其非空中序前趨結(jié)點(diǎn)間線索。
該算法應(yīng)附設(shè)一個(gè)指針pre始終指向剛剛訪問過的結(jié)點(diǎn)(pre的初值應(yīng)為NULL),而指針p
指示當(dāng)前正在訪問的結(jié)點(diǎn)。結(jié)點(diǎn)*pre是結(jié)點(diǎn)*p的前趨,而*p是*pre的后繼。
(2)將二叉樹按中序線索化的算法
typedefenum{Link,Thread}PointerTag;〃枚舉值Link和Thread分別為0,1
typedefstructnode{
DataTypcdata;
PointerTagItag,rtag;〃左右標(biāo)志
Structnode*lchild,*rchild;
}BinThrNode;\\線索二叉樹的結(jié)點(diǎn)類型
typedefBinThrNode*BinThrTree;
BinThrNode*pre=NULL;〃全局量
voidlnorderThreading(BinThrTreep)
{〃將二叉樹p中序線索化
if(p){〃p非空時(shí),當(dāng)前訪問結(jié)點(diǎn)是*p
InordcrThrcading(p->lchild);〃左子樹線索化
〃以下直至右子樹線索化之前相當(dāng)于遍歷算法中訪問結(jié)點(diǎn)的操作
p->ltag=(p->lchild)?Link:Thread;//左指針非空時(shí)左標(biāo)志為Link
//(即0),否則為Thread(即1)
p->rtag=(p->rchild)?Link:Thread;
*(pre){//若*p的前趨*pre存在
i?pre->rtag==Thread)〃若*p的前趨右標(biāo)志為線索
pre->rchild=p;〃令*pre的右線索指向中序后繼
if(p->ltag==Thread)//*p的左標(biāo)志為線索
p->lchild=pre;〃令*p的左線索指向中序前趨
}//完成處理*pre的線索
pre=p;〃令pre是下一訪問結(jié)點(diǎn)的中序前趨
InorderThrecding(p->rehild);〃右子樹線索化
}//endif
}//InordcrThreading
(3)算法分析
和中序遍歷算法一樣,遞歸過程中對每結(jié)點(diǎn)僅做一次訪問。因此對于n個(gè)結(jié)點(diǎn)的二叉樹,
算法的時(shí)間復(fù)雜度亦為O(n)。
3.二叉樹的前序線索化和后序線索化
前序線索化和后序線索化算法與二叉樹的中序線索化類似,具體可【參見參考書工
線索二叉樹的運(yùn)算
1.查找某結(jié)點(diǎn)*P在指定次序下的前趨和后繼結(jié)點(diǎn)
(1)在中序線索二叉樹中,查找結(jié)點(diǎn)*P的中序后繼結(jié)點(diǎn)
在中序線索二叉樹中,查找結(jié)點(diǎn)*P的中序后繼結(jié)點(diǎn)分兩種情形:
①若*p的右子樹空(即p->rtag為Thread),則p->rchild為右線索,直接指向*p的中序后繼。
【例】下圖的中序線索二叉樹中,結(jié)點(diǎn)D的中序后繼是A。
(a)中序線索二義樹
(b)中序線索鏈表
中序線索:叉樹及其存儲結(jié)構(gòu)
②若*p的右子樹非空(即p->rtag為Link),貝lj*p的中序后繼必是其右子樹中第一個(gè)中序遍
歷到的結(jié)點(diǎn)。也就是從*p的右孩子開始,沿該孩子的左鏈往下查找,直至找到一個(gè)沒有左孩子
的結(jié)點(diǎn)為止,該結(jié)點(diǎn)是*p的右子樹中"最左下"的結(jié)點(diǎn),即*P的中序后繼結(jié)點(diǎn)。
【例】上圖的中序線索二叉樹中:
A的中序后繼是F,它有右孩子;
F的中序后繼是H,它無右孩子;
B的中序后繼是D,它是B的右孩子。
在中序線索二義樹中求中序后繼結(jié)點(diǎn)的過程可【參見動(dòng)畫演示】,具體算法如下:
BinThrNode*InorderSuccessor(BinThrNode*p)
{//在中序線索樹中找結(jié)點(diǎn)*p的中序后繼,設(shè)p非空
BinThrNode*q;
if(p->rtag==Thread)//*p的右子樹為空
Returnp->rchiid;〃返回右線索所指的中序后繼
else{
q=p->rchild;//從*p的右孩子開始查找
while(q->ltag=Link)
q=q->lchild;〃左子樹非空時(shí),沿左鏈往下查找
returnq;〃當(dāng)q的左子樹為空時(shí),它就是最左下結(jié)點(diǎn)
}//endif
}
該算法的時(shí)間復(fù)雜度不超過樹的高度h,即0(h)。
(2)在中序線索二叉樹中查找結(jié)點(diǎn)*p的中序前趨結(jié)點(diǎn)
中序是一種對稱序,故在中序線索二又樹中查找結(jié)點(diǎn)*p的中序前趨結(jié)點(diǎn)與找中序后繼結(jié)點(diǎn)
的方法完全對稱。具體情形如下:
①若*p的左子樹為空,則p->lchild為左線索,直接指向*p的中序前趨結(jié)點(diǎn);
【例】上圖所示的中序線索二叉樹中,F(xiàn)結(jié)點(diǎn)的中序前趨結(jié)點(diǎn)是A
②若*p的左子樹非空,則從*p的左孩子出發(fā),沿右指針鏈往下查找,直到找到一個(gè)沒有右
孩子的結(jié)點(diǎn)為止。該結(jié)點(diǎn)是*p的左子樹中"最右下"的結(jié)點(diǎn),它是*p的左子樹中最后一個(gè)中序遍
歷到的結(jié)點(diǎn),即*p的中序前趨結(jié)點(diǎn)。
【例】上圖所示中序線索二叉樹中,結(jié)點(diǎn)E左子樹非空,其中序前趨結(jié)點(diǎn)是I
在中序線索二叉樹中求中序前趨結(jié)點(diǎn)的過程可【參見動(dòng)畫演示】,具體算法如下:
BinThrNode*lnorderpre(BinThrNode*p)
{〃在中序線索樹中找結(jié)點(diǎn)*p的中序前趨,設(shè)p非空
BinThrNode*q;
if(p->ltag=Thread)//*p的左子樹為空
returnp->lchild:〃返回左線索所指的中序前趨
else{
q=p->lchild:〃從*p的左孩子開始查找
while(q->rtag==Link)
q=q->rchild;〃右子樹非空時(shí),沿右鏈往下查找
returnq:〃當(dāng)q的右子樹為空時(shí),它就是最右下結(jié)點(diǎn)
}//endif
}
由上述討論可知:對于非線索二叉樹,僅從*p出發(fā)無法找到其中序前趨(或中序后繼),而
必須從根結(jié)點(diǎn)開始中序遍歷,才能找到*p的中序前趨(或中序后繼)。線索二叉樹中的線索使得查
找中序前趨和中序后繼變得簡單有效。
(3)在后序線索二叉樹中,查找指定結(jié)點(diǎn)*p的后序前趨結(jié)點(diǎn)
在后序線索二叉樹中,查找指定結(jié)點(diǎn)*p的后序前趨結(jié)點(diǎn)的具體規(guī)律是:
①若*P的左子樹為空,則p->lchild是前趨線索,指示其后序前趨結(jié)點(diǎn)。
【例】在下圖所示的后序線索二叉樹中,H的后序前趨是B,F的后序前趨是C。
后序戰(zhàn)索二叉樹
②若*P的左子樹非空,則p->lchild不是前趨線索。由于后序遍歷時(shí),根是在遍歷其左右子
樹之后被訪問的,故*P的后序前趨必是兩子樹中最后一個(gè)遍歷結(jié)點(diǎn)。
當(dāng)*P的右子樹非空時(shí),*p的右孩子必是其后序前趨
【例】在上圖所示的后序線索二叉樹中,A的后序前趨是E;
當(dāng)*p無右子樹時(shí),*p的后序前趨必是其左孩子
【例】在上圖所示的后序線索二叉樹中,E的后序前趨是F
(4)在后序線索二叉樹中,查找指定結(jié)點(diǎn)*p的后序后繼結(jié)點(diǎn)
具體的規(guī)律:
①若*p是根,則*p是該二叉樹后序遍歷過程中最后個(gè)訪問到的結(jié)點(diǎn)。*p的后序后繼為
空
②若*p是其雙親的右孩子,則*p的后序后繼結(jié)點(diǎn)就是其雙親結(jié)點(diǎn)
【例】上圖所示的后序線索二叉樹中,E的后序后繼是A。
③若*p是其雙親的左孩子,但*P無右兄弟,*p的后序后繼結(jié)點(diǎn)是其雙親結(jié)點(diǎn)
【例】上圖所示的后序線索二叉樹中,F(xiàn)的后序后繼是E。
④若*p是其雙親的左孩子,但*p有右兄弟,則*p的后序后繼是其雙親的右子樹中第一個(gè)
后序遍歷到的結(jié)點(diǎn),它是該子樹中"最左下的葉結(jié)點(diǎn)"
【例】上圖所示的后序線索二叉樹中,B的后序后繼是雙親A的右子樹中最左下的葉結(jié)點(diǎn)H
注意:
F是孩子樹中"最左下"結(jié)點(diǎn),但它不是葉子。
由上述討論中可知:在后序線索樹中,僅從*p出發(fā)就能找到其后序前趨結(jié)點(diǎn):要找*p的后
序后繼結(jié)點(diǎn),僅當(dāng)*p的右子樹為空時(shí),才能直接由*p的右線索p->rchild得到。否則必須知道*p
的雙親結(jié)點(diǎn)才能找到其后序后繼。因此,如果線索二叉樹中的結(jié)點(diǎn)沒有指向其雙親結(jié)點(diǎn)的指針,
就可能要從根開始進(jìn)行后序遍歷才能找到結(jié)點(diǎn)*P的后序后繼。由此,線索對查找指定結(jié)點(diǎn)的后
序后繼并無多大幫助。
(5)在前序線索二叉樹中,查找指定結(jié)點(diǎn)*p的前序后繼結(jié)點(diǎn)
【參見練習(xí)】
(6)在前序線索二叉樹中,查找指定結(jié)點(diǎn)*p的前序前趨結(jié)點(diǎn)
【參見參考書】
在前序線索二叉樹中,找某一點(diǎn)*P的前序后繼也很簡單,僅從*P出發(fā)就可以找到;但找其
前序前趨也必須知道*P的雙親結(jié)點(diǎn)。當(dāng)樹中結(jié)點(diǎn)未設(shè)雙親指針時(shí),同樣要進(jìn)行從根開始的前序
遍歷才能找到結(jié)點(diǎn)*P的前序前趨。
2.遍歷線索二叉樹
遍歷某種次序的線索二叉樹,只要從該次序下的開始結(jié)點(diǎn)開發(fā),反復(fù)找到結(jié)點(diǎn)在該次序下的
后繼,直至終端結(jié)點(diǎn)。
遍歷中序線索二叉樹算法:
voidTraverseInorderThrTrec(BinThrTrecp)
{//遍歷中序線索二叉樹
iRp){〃樹非空
while(p->ltag==Link)
p=p->lchild;〃從根往下找最左下結(jié)點(diǎn),即中序序列的開始結(jié)點(diǎn)
do{
printf("%c",p->data);〃訪問結(jié)點(diǎn)
p=InorderSuccessor(p);〃找*p的中序后繼
}while(p)
}//endif
}//TraverselnorderThrTree
分析:
①中序序列的終端結(jié)點(diǎn)的右線索為空,所以do語句的終止條件是p=NULL。
②該算法的時(shí)間復(fù)雜性為0(n)。因?yàn)槭欠沁f歸算法,常數(shù)因子上小于遞歸的遍歷算法。因
此,若對一棵二叉樹要經(jīng)常遍歷,或查找結(jié)點(diǎn)在指定次序下的前趨和后繼,則應(yīng)采用線索鏈表作
為存儲結(jié)構(gòu)為宜。
③以上介紹的線索二叉樹是一種全線索樹(即左右線索均要建立)。許多應(yīng)用中只要建立左
右線索中的一種。
④若在線索鏈表中增加一個(gè)頭結(jié)點(diǎn),令頭結(jié)點(diǎn)的左指針指向根,右指針指向其遍歷序列的
開始或終端結(jié)點(diǎn)會(huì)更方便。
樹和森林
樹、森林與二叉樹的轉(zhuǎn)換
樹或森林與二叉樹之間有一個(gè)自然的一一對應(yīng)關(guān)系。任何一個(gè)森林或一棵樹可惟一地對應(yīng)
到一棵二叉樹;反之,任何一棵二叉樹也能惟一地對應(yīng)到一個(gè)森林或一棵樹。
1.樹、森林到二叉樹的轉(zhuǎn)換
(1)將樹轉(zhuǎn)換為:叉樹
樹中每個(gè)結(jié)點(diǎn)最多只有?個(gè)最左邊的孩子(長子)和一個(gè)右鄰的兄弟。按照這種關(guān)系很自然
地就能將樹轉(zhuǎn)換成相應(yīng)的二叉樹:
①在所有兄弟結(jié)點(diǎn)之間加一連線;
②對每個(gè)結(jié)點(diǎn),除了保留與其長子的連線外,去掉該結(jié)點(diǎn)與其它孩子的連線。
【例11下面(a)圖所示的樹可轉(zhuǎn)換為(c)圖所示的二叉樹。具體轉(zhuǎn)換過程可【參見動(dòng)畫演示】
(a)(b)(c)
樹轉(zhuǎn)化成二又樹
注意:
由于樹根沒有兄弟,故樹轉(zhuǎn)化為二叉樹后,二叉樹的根結(jié)點(diǎn)的右子樹必為空。
(2)將一個(gè)森林轉(zhuǎn)換為二叉樹
具體方法是:
①將森林中的每棵樹變?yōu)槎鏄?/p>
②因?yàn)檗D(zhuǎn)換所得的二叉樹的根結(jié)點(diǎn)的右子樹均為空,故可將各二叉樹的根結(jié)點(diǎn)視為兄弟從
左至右連在一起,就形成了--棵二叉樹。
【例2】下圖中,左邊包含三棵樹的森林可轉(zhuǎn)換為右邊的二叉樹。
森林轉(zhuǎn)化為:又樹
具體轉(zhuǎn)換過程可【參見動(dòng)畫演示】
2.二叉樹到樹、森林的轉(zhuǎn)換
把二又樹轉(zhuǎn)換到樹和森林自然的方式是:若結(jié)點(diǎn)x是雙親y的左孩子,則把x的右孩子,
右孩子的右孩子,…,都與y用連線連起來,最后去掉所有雙親到右孩子的連線。
[例3]下圖的森林就是由例2中二叉樹轉(zhuǎn)換成的。
1周(c)的:一叉樹轉(zhuǎn)化為森林
具體轉(zhuǎn)換過程可【參見動(dòng)畫演示】
樹的存儲結(jié)構(gòu)
本節(jié)僅討論樹的三種常用表示法。
1.雙親鏈表表示法
雙親鏈表表示法利用樹中每個(gè)結(jié)點(diǎn)的雙親唯一性,在存儲結(jié)點(diǎn)信息的同時(shí),為每個(gè)結(jié)點(diǎn)附
設(shè)一個(gè)指向其雙親的指針parent,惟一地表示任何一棵樹。
(1)雙親鏈表表示法的實(shí)現(xiàn)
方法①用動(dòng)態(tài)鏈表實(shí)現(xiàn)
方法②用向量表示——更為方便
(2)雙親鏈表向量表示的形式說明
^defineMaxTreeSize100〃向量空間的大小,由用戶定義
typedefcharDataType;〃應(yīng)由用戶定義
typedefstruct{
DataTypedata;〃結(jié)點(diǎn)數(shù)據(jù)
intparent;〃雙親指針,指示結(jié)點(diǎn)的雙親在向量中的位置
JPTreeNode;
typedefstruct{
PTreeNodenodes[MaxTreeSize];
intn;〃結(jié)點(diǎn)總數(shù)
JPTree;
PTreeT;//T是雙親鏈表
注意:
若T.nodes[i].parent=j,則T.nodes[i]的雙親是T.nodes[j]0
(3)雙親鏈表表示實(shí)例
【例】圖6.17(a)的雙親鏈表表示如下面數(shù)組所示。
MaxTrecSizc-1
下標(biāo)01234567891
ABCDEFG11IJ???
-1000112333???
圖6.17樹轉(zhuǎn)化為.又樹(a)所示.義樹的雙親鏈表T
分析:
E和F所在結(jié)點(diǎn)的雙親域是1,它們的雙親結(jié)點(diǎn)在向量中的位置是1,即B是它們的雙親。
注意:
①根無雙親,其parent域?yàn)門。
②雙親鏈表表示法中指針parent向上鏈接,適合求指定結(jié)點(diǎn)的雙親或祖先(包括根);求
指定結(jié)點(diǎn)的孩子或其它后代時(shí),可能要遍歷整個(gè)數(shù)組。
2.孩子鏈表表示法
(1)結(jié)點(diǎn)結(jié)構(gòu)
①定長結(jié)點(diǎn)
即樹中每個(gè)結(jié)點(diǎn)均按樹的度k來設(shè)置指針。
n個(gè)結(jié)點(diǎn)的樹-共有n*k個(gè)指針域,而樹中只有n-1條邊,故樹中的空指針數(shù)目為
kn-(n-l)=n(k-l)+l(k越大,浪費(fèi)的空間越多)。
②不定長結(jié)點(diǎn)
即樹中每個(gè)結(jié)點(diǎn)按本結(jié)點(diǎn)的度來設(shè)置指針數(shù),并在結(jié)點(diǎn)中增設(shè)一個(gè)度數(shù)域degree指出該
結(jié)點(diǎn)包含的指針數(shù)。
各結(jié)點(diǎn)不等長,雖然節(jié)省了空間,但是給運(yùn)算帶來不便。
(2)孩子鏈表表示法
孩子鏈表表示法是為樹中每個(gè)結(jié)點(diǎn)設(shè)置一個(gè)孩子鏈表,并將這些結(jié)點(diǎn)及相應(yīng)的孩子鏈表的
頭指針存放在一個(gè)向量中。
①孩子鏈表表示法的類型說明
〃以下的DataType和MaxTreeSize由用戶定義
typedefstructCNode{〃子鏈表結(jié)點(diǎn)
intchild;〃孩子結(jié)點(diǎn)在向量中對應(yīng)的序號
structCNode*next;
JCNode;
typedefstruct{
DataTypedata;〃存放樹中結(jié)點(diǎn)數(shù)據(jù)
CNode*firstchild;〃孩子鏈表的頭指針
JPTNode;
typedefstruct{
PTNodenodes[MaxTreeSize];
Intn,root;〃n為結(jié)點(diǎn)總數(shù),root指出根在向量中的位置
(CTree;
CtreeT;//T為孩子鏈表表示
注意:
當(dāng)結(jié)點(diǎn)T.nodesli]為葉子時(shí),其孩子鏈表為空,BPT.nodes[i].firstchild=NULL<)
②孩子鏈表表示法實(shí)例
【例】圖6.17(a)所示樹的孩子鏈表表示T如下面(a)圖所示。
A
圖6.17樹轉(zhuǎn)化為:叉樹(a)中樹的孩子鏈表表示法
注意:
①孩子結(jié)點(diǎn)的數(shù)據(jù)域僅存放了它們在向量空間的序號。
②與雙親鏈表表示法相反,孩子鏈表表示便于實(shí)現(xiàn)涉及孩子及其子孫的運(yùn)算,但不便于
實(shí)現(xiàn)與雙親有關(guān)的運(yùn)算。
③將雙親鏈表表示法和孩廣鏈表表示法結(jié)合起來,可形成雙親孩子鏈表表示法。
【例】上面的(b)圖就是用雙親鏈表表示法來存儲圖6.17(a)中的樹。
3.孩子兄弟鏈表表示法
(1)表示方法
在存儲結(jié)點(diǎn)信息的同時(shí),附加兩個(gè)分別指向該結(jié)點(diǎn)最左孩子和右鄰兄弟的指針域
leftmostchild和rightsibling,即可得樹的孩子兄弟鏈表表示。
(2)表示實(shí)例
【例】圖6.17(a)中樹的孩子兄弟鏈表如下圖所示。
A
圖6.17(a)
圖6.17樹轉(zhuǎn)化為:叉樹(a)中樹的孩子兄弟鏈表
注意:
這種存儲結(jié)構(gòu)的最大優(yōu)點(diǎn)是:它和二叉樹的二叉鏈表表示完全一樣。可利用二叉樹的算法
來實(shí)現(xiàn)對樹的操作。
樹的遍歷
1.樹T的前序遍歷定義;
若樹T非空,則:
①訪問根結(jié)點(diǎn)R;
②依次前序遍歷根R的各子樹T”
2.樹的后序遍歷定義:
若樹T非空,則:
①依次后序遍歷根T的各子樹r,Tz,???,Tk;
②訪問根結(jié)點(diǎn)Ro
【例】對下面的(a)圖中的樹進(jìn)行前序遍歷和后序遍歷,得到的前序序列和后序序列分別是ABCDE
和BDCEAo
AA
樹和對應(yīng)的二義樹
注意:
①前序遍歷一棵樹恰好等價(jià)于前序遍歷該樹對應(yīng)的二叉樹
②后序遍歷樹恰好等價(jià)于中序遍歷該樹對應(yīng)的二叉樹。
森林的兩種遍歷方法
1.前序遍歷森林
若森林非空,則:
①訪問森林中第一棵樹的根結(jié)點(diǎn);
②前序遍歷第一棵樹中根結(jié)點(diǎn)的各子樹所構(gòu)成的森林
③前序遍歷除第一棵樹外其它樹構(gòu)成的森林。
2.后序遍歷森林
若森林非空,貝IJ:
①后序遍歷森林中第?棵樹的根結(jié)點(diǎn)的各子樹所構(gòu)成的森林;
②訪問第一棵樹的根結(jié)點(diǎn);
③后序遍歷除第一棵樹外其它樹構(gòu)成的森林。
注意:
①前序遍歷森林等同于前序遍歷該森林對應(yīng)的二叉樹
②后序遍歷森林等同于中序遍歷該森林對應(yīng)的二叉樹
【例】對下面(a)圖中所示的森林進(jìn)行前序遍歷和后序遍歷,則得到該森林的前序序列和后序序
列分別為ABCDEFICJH和BDCAIFJGHE?而(b)圖所示二叉樹的前序序列和中序序列也分別為
ABCDEFICJH和BDCAIFJGHE,
(a)森林(b)對應(yīng)的.又樹
森林和對陶的二叉樹
③當(dāng)用二叉鏈表作樹和森林的存儲結(jié)構(gòu)時(shí),樹和森林的前序遍歷和后遍歷,可用二叉樹
的前序遍歷和中序遍歷算法來實(shí)現(xiàn)。
哈夫曼樹及其應(yīng)用
最優(yōu)二叉樹概念
1.樹的路徑長度
樹的路徑長度是從樹根到樹中每一結(jié)點(diǎn)的路徑長度之和。在結(jié)點(diǎn)數(shù)目相同的二叉樹中,完
全二叉樹的路徑長度最短。
2.樹的帶權(quán)路徑長度(WeightedPathLengthofTree,簡記為WPL)
結(jié)點(diǎn)的權(quán):在一些應(yīng)用中,賦予樹中結(jié)點(diǎn)的?個(gè)有某種意義的實(shí)數(shù)。
結(jié)點(diǎn)的帶權(quán)路徑長度:結(jié)點(diǎn)到樹根之間的路徑長度與該結(jié)點(diǎn)上權(quán)的乘積。
樹的帶權(quán)路徑長度(WeightedPathLengthofTree):定義為樹中所有葉結(jié)點(diǎn)的帶權(quán)路徑長
度之和,通常記為:
?!鲂?,丸
其中:
n表示葉子結(jié)點(diǎn)的數(shù)目
w;和L分別表示葉結(jié)點(diǎn)k,的權(quán)值和根到結(jié)點(diǎn)k:之間的路徑長度。
樹的帶權(quán)路徑長度亦稱為樹的代價(jià)。
3.最優(yōu)二叉樹或哈夫曼樹
在權(quán)為W,wz,…,的n個(gè)葉子所構(gòu)成的所有二叉樹中,帶權(quán)路徑長度最小(即代價(jià)最
小)的二叉樹稱為最優(yōu)二叉樹或哈夫曼樹。
【例】給定4個(gè)葉子結(jié)點(diǎn)a,b,c和d,分別帶權(quán)7,5,2和4。構(gòu)造如下圖所示的三棵二叉
樹(還有許多棵),它們的帶權(quán)路徑長度分別為:
(a)WPL=7*2+5*2+2*2+4*2=36
(b)WPL=7*3+5*3+2*1+4*2=46
(c)WPL=7*1+5*2+2*3+4*3=35
具有不同WPL的二叉樹(結(jié)點(diǎn)旁的數(shù)字為權(quán))
注意:
①葉子上的權(quán)值均相同時(shí),完全二叉樹一定是最優(yōu)二叉樹,否則完全二叉樹不一定是最優(yōu)
二叉樹。
②最優(yōu)二叉樹中,權(quán)越大的葉子離根越近。
③最優(yōu)二叉樹的形態(tài)不唯一,WPL最小
構(gòu)造最優(yōu)二叉樹
1.哈夫曼算法
哈夫曼首先給出了對于給定的葉子數(shù)目及其權(quán)值構(gòu)造最優(yōu)二叉樹的方法,故稱其為哈夫曼
算法。其基本思想是:
(1)根據(jù)給定的n個(gè)權(quán)值wi,w2,???,w”構(gòu)成n棵二叉樹的森林F={T”T2,???,T?),其中每
棵二叉樹"中都只有一個(gè)權(quán)值為w,的根結(jié)點(diǎn),其左右子樹均空。
(2)在森林F中選出兩棵根結(jié)點(diǎn)權(quán)值最小的樹(當(dāng)這樣的樹不止兩棵樹時(shí),可以從中任選兩
棵),將這兩棵樹合并成一棵新樹,為了保證新樹仍是二叉樹,需要增加一個(gè)新結(jié)點(diǎn)作為新樹的
根,并將所選的兩棵樹的根分別作為新根的左右孩子(誰左,誰右無關(guān)緊要),將這兩個(gè)孩子的權(quán)
值之和作為新樹根的權(quán)值。
(3)對新的森林F重復(fù)(2),直到森林F中只剩下一棵樹為止。這棵樹便是哈夫曼樹。
用哈夫曼算法構(gòu)造哈夫曼樹的過程見【動(dòng)畫演示】。
注意:
①初始森林中的n棵二叉樹,每棵樹有一個(gè)孤立的結(jié)點(diǎn),它們既是根,又是葉子
②n個(gè)葉子的哈夫曼樹要經(jīng)過n-1次合并,產(chǎn)生n-1個(gè)新結(jié)點(diǎn)。最終求得的哈夫曼樹中共
有2n-l個(gè)結(jié)點(diǎn)。
③哈夫曼樹是嚴(yán)格的二叉樹,沒有度數(shù)為1的分支結(jié)點(diǎn)。
2.哈夫曼樹的存儲結(jié)構(gòu)及哈夫曼算法的實(shí)現(xiàn)
(D哈夫曼樹的存儲結(jié)構(gòu)
用一個(gè)大小為2n-l的向量來存儲哈夫曼樹中的結(jié)點(diǎn),其存儲結(jié)構(gòu)為:
^definen100//葉子數(shù)目
#definem2*nT//樹中結(jié)點(diǎn)總數(shù)
typedefstruct{〃結(jié)點(diǎn)類型
floatweight;〃權(quán)值,不妨設(shè)權(quán)值均大于零
intIchild,rchild,parent;〃左右孩子及雙親指針
}HTNode;
typedefHTNodeHuffmanTree[m];〃HuffmanTree是向量類型
注意:
因?yàn)镃語言數(shù)組的下界為0,故用T表示空指針。樹中某結(jié)點(diǎn)的lchild>rchild和parent
不等于T時(shí),它們分別是該結(jié)點(diǎn)的左、右孩子和雙親結(jié)點(diǎn)在向量中的下標(biāo)。
這里設(shè)置parent域有兩個(gè)作用:其一是使查找某結(jié)點(diǎn)的雙親變得簡單;其二是可通過判
定parent的值是否為T來區(qū)分根與非根結(jié)點(diǎn)。
(2)哈夫曼算法的簡要描述
在上述存儲結(jié)構(gòu)上實(shí)現(xiàn)的哈夫曼算法可大致描述為(設(shè)T的類型為HuffmanTree):
(1)初始化
將T[0.?mT]中2n-l個(gè)結(jié)點(diǎn)里的三個(gè)指針均置為空(即置為-1),權(quán)值置為0。
(2)輸人
讀人n個(gè)葉子的權(quán)值存于向量的前n個(gè)分量(即T[0..n-1])中。它們是初始森林中n個(gè)
孤立的根結(jié)點(diǎn)上的權(quán)值。
(3)合并
對森林中的樹共進(jìn)行n-1次合并,所產(chǎn)生的新結(jié)點(diǎn)依次放人向量T的第i個(gè)分量中
(nWiWm-1)。每次合并分兩步:
①在當(dāng)前森林T[0.?iT]的所有結(jié)點(diǎn)中,選取權(quán)最小和次小的兩個(gè)根結(jié)點(diǎn)[pl]和T[p2]
作為合并對象,這里OWpl,p2Wi-L
②將根為T[pl]和T[p2]的兩棵樹作為左右子樹合并為一棵新的樹,新樹的根是新結(jié)點(diǎn)
T[i]?具體操作:
將T[pl]和T[p2]的parent置為i,
將T[i]的Ichild和rchild分別置為pl和p2
新結(jié)點(diǎn)T[i]的權(quán)值置為T[pl]和
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2012年湖北十堰中考滿分作文《因?yàn)橛心恪?0
- 2024年浙教版高二英語上冊階段測試試卷710
- 2020-2021學(xué)年河北省秦皇島市開發(fā)區(qū)四年級下學(xué)期期末語文真題及答案
- 2024年冀教新版九年級語文下冊階段測試試卷含答案242
- 2024年冀少新版八年級地理上冊階段測試試卷含答案775
- 2024年上外版八年級歷史上冊月考試卷含答案666
- 柴油機(jī)氣門彈簧課程設(shè)計(jì)
- 人教版高中物理必修第三冊第十三章電磁感應(yīng)與電磁波初步13-4電磁波的發(fā)現(xiàn)及應(yīng)用練習(xí)含答案
- 2024年演出票務(wù)預(yù)訂合同3篇
- 電路課程設(shè)計(jì)作品
- 人教部編本八年級語文上冊第六單元復(fù)習(xí)課件共26張
- 2024年土地管理法
- 框架玻璃幕墻施工工藝
- 《水產(chǎn)種質(zhì)資源保護(hù)區(qū)生態(tài)功能評估方法》
- 韻達(dá)云倉方案
- 23秋國家開放大學(xué)《法律職業(yè)倫理》形考任務(wù)1-3參考答案
- 2023-2024學(xué)年福建省廈門市思明區(qū)重點(diǎn)中學(xué)七年級(上)期末數(shù)學(xué)試卷(含解析)
- 預(yù)防血栓藥物課件
- 2022讀《整本書閱讀的六項(xiàng)核心技術(shù)》有感
- 2023年高考英語真題題源解密(新高考卷)專題03 閱讀理解C篇(說明文)(原題版)
- 公司駕駛員安全駕駛培訓(xùn)
評論
0/150
提交評論