树的知识总结
樹TREE
?如圖所示:?
樹的概念
?
需要注意:
樹( tree)是n(n≥0)個(gè)結(jié)點(diǎn)的有限集合。當(dāng)n=0時(shí),稱為空樹;任意一棵非空樹滿足以下條件:
1)有且僅有一個(gè)特定的稱為根(root)的結(jié)點(diǎn)。
(2)當(dāng)n>1時(shí),除根結(jié)點(diǎn)之外的其余結(jié)點(diǎn)被分成m(m>0)個(gè)互不相交的有限集合:T1,T2,…,Tm,其中每個(gè)集合又是一棵樹,并稱為這個(gè)根結(jié)點(diǎn)的子樹(subtree)。需要強(qiáng)調(diào)的是,樹中根結(jié)點(diǎn)的子樹之間是互不相交的。
?
結(jié)點(diǎn)分類:
結(jié)點(diǎn)之間的關(guān)系:
(1)結(jié)點(diǎn)的度、樹的度
某結(jié)點(diǎn)所擁有的子樹的個(gè)數(shù)為該節(jié)點(diǎn)的度,樹的度是每個(gè)結(jié)點(diǎn)的度中的最大值。
(2)葉子結(jié)點(diǎn)、分支結(jié)點(diǎn)
度為0的結(jié)點(diǎn)稱為葉子結(jié)點(diǎn)或者稱為終端結(jié)點(diǎn):度不為0的結(jié)點(diǎn)稱為分支結(jié)點(diǎn),也稱為非終端結(jié)點(diǎn)。 (3)孩子結(jié)點(diǎn)、雙親結(jié)點(diǎn)、兄弟結(jié)點(diǎn)
某結(jié)點(diǎn)的子樹的根結(jié)點(diǎn)稱為該結(jié)點(diǎn)的孩子結(jié)點(diǎn)(child node):反之,該結(jié)點(diǎn)稱為其孩子結(jié)點(diǎn)的雙親結(jié)點(diǎn)(parent node):具有同一個(gè)雙親的孩子結(jié)點(diǎn)互稱為兄弟結(jié)點(diǎn)(brother node)。
?
樹其他相關(guān)概念:
?
?
樹具有的特點(diǎn):
?
樹的相關(guān)術(shù)語:
?
樹的存儲(chǔ)結(jié)構(gòu)
1.雙親表示法:
樹除了根結(jié)點(diǎn)外,其余每個(gè)結(jié)點(diǎn),它不一定有孩子,但一定有且僅有一個(gè)雙親。 假設(shè)以一組連續(xù)空間存儲(chǔ)樹的結(jié)點(diǎn),同時(shí)在每個(gè)結(jié)點(diǎn)中,附設(shè)一個(gè)指示器指示其雙親結(jié)點(diǎn)到鏈表中的位置。也就是說,每個(gè)結(jié)點(diǎn)除了知道自己是誰以外,還知道它的雙親在哪里。它的結(jié)點(diǎn)結(jié)構(gòu)如圖6-4-1所示。 其中data是數(shù)據(jù)域,存儲(chǔ)結(jié)點(diǎn)的數(shù)據(jù)信息,parent是指針域,存儲(chǔ)該結(jié)點(diǎn)的雙親在數(shù)組中的下標(biāo)。 以下是雙親表示法的結(jié)點(diǎn)結(jié)構(gòu)定義代碼:
/*樹的雙親表示法結(jié)點(diǎn)結(jié)構(gòu)定義*/ #define MAX_TREE_SIZE 100 typedef int TElemType;/*樹結(jié)點(diǎn)的數(shù)據(jù)類型,暫且定為整型*/ typedef struct PTNode/*結(jié)點(diǎn)結(jié)構(gòu)*/ {TElemType data;/*結(jié)點(diǎn)數(shù)據(jù)*/int parent;/*雙親位置*/ }PTNode; typedef struct/*樹結(jié)構(gòu)*/ {PTNode nodes[MAX_TREE_SIZE];/*結(jié)點(diǎn)數(shù)組*/int r, n;/*根的位置和結(jié)點(diǎn)數(shù)*/ }PTree; ?由于根結(jié)點(diǎn)是沒有雙親的,所以我們約定根結(jié)點(diǎn)的位置域設(shè)置為-1,這就意味著我們所有的結(jié)點(diǎn)都存有它的雙親位置。如圖6-4-1的樹結(jié)構(gòu)和表6-4-2的樹雙親表示法所示。
?
這樣的存儲(chǔ)結(jié)構(gòu)我們可以根據(jù)結(jié)點(diǎn)的parent指針很容易找到它的雙親位置,所用的時(shí)間復(fù)雜度為O(1),知道parent=-1時(shí)表示找到了樹結(jié)點(diǎn)的根。但如果要知道結(jié)點(diǎn)的孩子是什么,需要遍歷整個(gè)結(jié)構(gòu)。
為了更方便地找到結(jié)點(diǎn)的孩子,我們增加一個(gè)結(jié)點(diǎn)最左邊孩子的域,稱為長子域,這樣就可以很容易地得到結(jié)點(diǎn)地孩子。如果沒有孩子地結(jié)點(diǎn),這個(gè)長子域就設(shè)置為-1,如表6-4-3所示。
對于有0個(gè)或1個(gè)孩子結(jié)點(diǎn)來說,這樣地結(jié)構(gòu)是解決了要找孩子結(jié)點(diǎn)的問題了。甚至是有兩個(gè)孩子,知道了長子是誰,另一個(gè)當(dāng)然是次子了。 為了體現(xiàn)各兄弟之間的關(guān)系,可以增加一個(gè)有兄弟域來體現(xiàn)兄弟的關(guān)系,也就是說每一個(gè)結(jié)點(diǎn)如果它存在右兄弟,則記錄下右兄弟的下標(biāo)。同樣地,如果右兄弟不存在,則賦值為-1,如表6-4-4所示。
但如果結(jié)點(diǎn)地孩子很多,超過了2個(gè),我們又關(guān)注結(jié)點(diǎn)的孩子,又關(guān)注結(jié)點(diǎn)的雙親,還關(guān)注結(jié)點(diǎn)的右兄弟,而且對時(shí)間遍歷要求還比較高,那么我們可以把此結(jié)構(gòu)擴(kuò)展為有雙親域、長子域、還有右兄弟域(即用 空間換取了時(shí)間)。存儲(chǔ)結(jié)構(gòu)的設(shè)計(jì)是一個(gè)非常長靈活的過程。一個(gè)存儲(chǔ)結(jié)構(gòu)設(shè)計(jì)得是否合理,取決于基于該存儲(chǔ)結(jié)構(gòu)的運(yùn)算是否適合、是否方便,時(shí)間復(fù)雜度好不好等。注意也不是越多越好,有需要再設(shè)計(jì)相應(yīng)的結(jié)構(gòu)。
2.孩子表示法:
換一種完全不同的考慮方法。由于樹中每個(gè)結(jié)點(diǎn)可能有多棵子樹,可以考慮用多重鏈表,即每個(gè)結(jié)點(diǎn)有多個(gè)指針域,其中每個(gè)指針指向一棵子樹的根結(jié)點(diǎn),我們把這種方法叫作多重鏈表表示法。不過樹的每個(gè)結(jié)點(diǎn)的度,也就是它的孩子的個(gè)數(shù)是不相同的。所以可以設(shè)計(jì)兩種方案來解決。
方案一: 一種是指針域的個(gè)數(shù)就等于樹的度,因?yàn)闃涞亩仁菢涓鱾€(gè)結(jié)點(diǎn)度的最大值。其結(jié)構(gòu)如下表:
其中data是數(shù)據(jù)域,child1到childd是指針域,用來指向該結(jié)點(diǎn)的孩子結(jié)點(diǎn)。 對于圖6-4-1的樹來說,樹的度是3,所以指針域的個(gè)數(shù)是3,這種方法實(shí)現(xiàn)如下圖:
?
這種方法對于樹中各結(jié)點(diǎn)的度相差很大時(shí),顯然是很浪費(fèi)空間的,因?yàn)橛泻芏嗟慕Y(jié)點(diǎn),它的指針域都是空的。不過如果樹中各結(jié)點(diǎn)的度相差很小時(shí),那就意味著開辟的空間被充分利用了,這時(shí)存儲(chǔ)結(jié)構(gòu)的缺點(diǎn)反而變成優(yōu)點(diǎn)了。 既然很多指針域都可能為空,那就按需分配空間,即引出了第二中分配方法。
方案二: 第二種方案每個(gè)結(jié)點(diǎn)指針域的個(gè)數(shù)等于該結(jié)點(diǎn)的度,專門取一個(gè)位置來存儲(chǔ)結(jié)點(diǎn)指針域的個(gè)數(shù),其結(jié)構(gòu)如下表所示:
其中data是數(shù)據(jù)域,degree為度域,也就是存儲(chǔ)該結(jié)點(diǎn)的孩子結(jié)點(diǎn)的個(gè)數(shù),child1到childd是指針域,用來指向該結(jié)點(diǎn)的孩子結(jié)點(diǎn)。 對于圖6-4-2的樹來說,該方法實(shí)現(xiàn)如下圖:
這種方法克服了浪費(fèi)空間的缺點(diǎn),對空間利用率是很高了,但是由于各個(gè)結(jié)點(diǎn)的鏈表是不同的結(jié)構(gòu),加上要維護(hù)結(jié)點(diǎn)的度的數(shù)值,在運(yùn)算上就會(huì)帶來時(shí)間上的損耗。 為了既可以減少空指針的浪費(fèi)又能使結(jié)點(diǎn)結(jié)構(gòu)相同,可以把每個(gè)結(jié)點(diǎn)放到一個(gè)順序存儲(chǔ)的結(jié)構(gòu)數(shù)組中,再對每個(gè)結(jié)點(diǎn)的孩子建立一個(gè)單鏈表體現(xiàn)它們的關(guān)系。 這就是我們要講的孩子表示法。具體辦法是,將每個(gè)結(jié)點(diǎn)的孩子結(jié)點(diǎn)排列起來,以單鏈表作存儲(chǔ)結(jié)構(gòu),則n個(gè)結(jié)點(diǎn)有n個(gè)孩子鏈表,如果是葉子結(jié)點(diǎn),則此單鏈表為空。然后n個(gè)頭指針又組成一個(gè)線性表,采用順序存儲(chǔ)結(jié)構(gòu),存放進(jìn)一個(gè)一維數(shù)組中,如圖6-4-4所示。
為此,設(shè)計(jì)兩種結(jié)點(diǎn)結(jié)構(gòu),一個(gè)是孩子鏈表的孩子結(jié)點(diǎn),如下表:
其中,child是數(shù)據(jù)域,用來存儲(chǔ)某個(gè)結(jié)點(diǎn)在表頭數(shù)組中的下標(biāo)。next是指針域,用來存儲(chǔ)指向某結(jié)點(diǎn)的下一個(gè)孩子結(jié)點(diǎn)的指針。 另一個(gè)是表頭數(shù)組 的表頭結(jié)點(diǎn),如下表 :
其中data是數(shù)據(jù)域,存儲(chǔ)某結(jié)點(diǎn)的數(shù)據(jù)信息。firstchild是頭指針域,存儲(chǔ)該結(jié)點(diǎn)的孩子鏈表的頭指針。 以下是孩子結(jié)點(diǎn)表示法的結(jié)構(gòu)定義代碼:
/*樹的孩子表示法結(jié)構(gòu)定義*/ typedef struct CTNode/*孩子結(jié)點(diǎn)*/ {int child;/*孩子結(jié)點(diǎn)在表頭數(shù)組中的下標(biāo)*/CTNode* next;/*指向某結(jié)點(diǎn)的下一個(gè)孩子結(jié)點(diǎn)*/ }*ChildPtr; typedef struct/*表頭數(shù)組 */ {TElemType data;/*某結(jié)點(diǎn)的數(shù)據(jù)域,存儲(chǔ)某節(jié)點(diǎn)的數(shù)據(jù)信息*/ChildPtr firstchild;/*指向某結(jié)點(diǎn)的第一個(gè)孩子結(jié)點(diǎn)*/ }CTBox; typedef struct/*樹結(jié)構(gòu)*/ {CTBox nodes[MAX_TREE_SIZE];/*結(jié)點(diǎn)數(shù)組*/int r, n;/*根的位置和結(jié)點(diǎn)數(shù)*/ }CTree這樣的結(jié)構(gòu)對于要找某個(gè)結(jié)點(diǎn)的孩子,或者找某個(gè)結(jié)點(diǎn)的兄弟,只需要查找這個(gè)結(jié)點(diǎn)的孩子單鏈表即可。對于遍歷整棵樹也是很方便,對頭結(jié)點(diǎn)的數(shù)組進(jìn)行循環(huán)即可。 但是,這也存在著問題,要想知道某個(gè)結(jié)點(diǎn)的雙親是誰,需要整棵樹遍歷才行,比較麻煩。 把雙親表示法和孩子表示法綜合,如下圖:
把這種方法稱為孩子雙親表示法,應(yīng)該算是孩子表示法的改進(jìn),其結(jié)構(gòu)代碼定義如下:
typedef struct/*表頭數(shù)組 */ {TElemType data;/*某結(jié)點(diǎn)的數(shù)據(jù)域,存儲(chǔ)某節(jié)點(diǎn)的數(shù)據(jù)信息*/ChildPtr firstchild;/*指向某結(jié)點(diǎn)的第一個(gè)孩子結(jié)點(diǎn)*/int parent;/*雙親位置*/ }CTBox;其余和孩子表示法相同。
3.孩子兄弟表示法:
觀察發(fā)現(xiàn) 任意發(fā)現(xiàn),任意一棵樹,它的結(jié)點(diǎn)的第一個(gè)孩子如果存在就是唯一的,它的右兄弟如果存在也是唯一的。因此,我們設(shè)置兩個(gè)指針 分別指向該結(jié)點(diǎn)的第一個(gè)孩子和此節(jié)點(diǎn)的右兄弟。 結(jié)點(diǎn)結(jié)構(gòu)如下表:
其中data是數(shù)據(jù)域,firstchild為指針域,存儲(chǔ)該結(jié)點(diǎn)的第一個(gè)孩子結(jié)點(diǎn)的存儲(chǔ)地址,rightsib是指針域,存儲(chǔ)該結(jié)點(diǎn)的右兄弟結(jié)點(diǎn)的存儲(chǔ)地址。 結(jié)構(gòu)定義代碼如下 :
/*樹的孩子兄弟法結(jié)構(gòu)定義 */ typedef struct {TElemType data;/*數(shù)據(jù)域*/struct CSNode* firstchild, *rightsib;/*指向第一個(gè)孩子結(jié)點(diǎn)和右兄弟結(jié)點(diǎn)*/ }CSNode,*CSTree;對于圖6-4-1的樹來說,這種方法實(shí)現(xiàn)的示意圖如下圖所示:
這樣表示方法,給查找某個(gè)結(jié)點(diǎn)的某個(gè)孩子帶來了方便,只需要通過firstchild找到此結(jié)點(diǎn)的長子,然后再通過此結(jié)點(diǎn)的rightsib找到它的二弟,接著一直下去,直到找到具體的孩子。如果要查找某個(gè)結(jié)點(diǎn)的雙親,可以再增加一個(gè)parent指針域來解決快速查找雙親的問題。 其實(shí)這個(gè)表示法最大的好處是它把一棵復(fù)雜的樹變成了一棵二叉樹。把圖6-4-6變形即可得到下圖這個(gè)樣子:
這樣就可以充分利用二叉樹的性質(zhì)和算法來處理這棵樹了 。
二叉樹
?
二叉樹的定義
對于這種在某個(gè)階段都是兩種結(jié)果的情形,比如開和關(guān)、0和1、真和假、上和下、對與錯(cuò)、正面和反面等,都適合用樹狀結(jié)構(gòu)來建模,而這種樹是很特殊的樹狀結(jié)構(gòu),叫做二叉樹。 二叉樹(Binary Tree)是n(n≥0)個(gè)結(jié)點(diǎn)的有限集合,該集合或者為空集(稱為空二叉樹),或者由一個(gè)根結(jié)點(diǎn)和兩棵互不相交的、分別稱為根結(jié)點(diǎn)的左子樹和右子樹的二叉樹組成。如下圖:
(1)二叉樹特點(diǎn)
二叉樹的特點(diǎn)有:
1)每個(gè)結(jié)點(diǎn)最多有兩棵子樹,所以二叉樹中不存在度大于2的結(jié)點(diǎn)。注意不是只有兩棵子樹,而是最多有。沒有子樹或者有一棵子樹也是可以的。
2)左子樹和右子樹 是有順序的,次序不能任意顛倒。
3)即使樹中某結(jié)點(diǎn)只有一棵子樹,也要區(qū)分它是左子樹還是右子樹。如下圖,樹1和樹2是同一棵樹,但它們卻是不同的二叉樹。
二叉樹具有五種基本形態(tài):
1)空二叉樹;
2)只有一個(gè)根結(jié)點(diǎn);
3)根結(jié)點(diǎn)只有左子樹;
4)根結(jié)點(diǎn)只有右子樹;
5)根結(jié)點(diǎn)既有左子樹又有右子樹。
?
如果有三個(gè)結(jié)點(diǎn),則存在如下物種二叉樹:
(2)特殊二叉樹
1)斜樹 斜樹一定是斜的,但往哪里斜還是有講究的。所有結(jié)點(diǎn)都只有左子樹的二叉樹叫左斜樹。所有結(jié)點(diǎn)都只有右子樹的二叉樹叫右斜樹。這兩者統(tǒng)稱為斜樹。圖6-5-4中的樹2就是左斜樹,樹3就是右斜樹。斜樹有很明顯的特點(diǎn),就是每一層都只有結(jié)點(diǎn),結(jié)點(diǎn)的個(gè)數(shù)與二叉樹的深度相同。 其實(shí)線性表的結(jié)構(gòu)可以理解為樹的一種極為特殊的表現(xiàn)形式。
2)滿二叉樹 在一棵二叉樹中,如果所有分支結(jié)點(diǎn)都存在左子樹和右子樹,并且所有葉子都在同一層上,這樣的二叉樹稱為滿二叉樹,如下圖:
?
滿二叉樹的特點(diǎn)有: 葉子只出現(xiàn)在最下一層,出現(xiàn)在其他層就不可能達(dá)到平衡; 非葉子結(jié)點(diǎn)的度一定是2; 在同樣深度的二叉樹中,滿二叉樹的結(jié)點(diǎn)個(gè)數(shù)最大,葉子個(gè)數(shù)也最多。
3)完全二叉樹
二叉樹的性質(zhì)
(1)第i層的至多結(jié)點(diǎn)個(gè)數(shù)
(2)深度為k的結(jié)點(diǎn)個(gè)數(shù)
(3)終端結(jié)點(diǎn)個(gè)數(shù)和度為2的結(jié)點(diǎn)個(gè)數(shù)的關(guān)系
(4)具有n個(gè)結(jié)點(diǎn)的完全二叉樹的深度
(5)判斷根、雙親、左右孩子的編號(hào)和總的結(jié)點(diǎn)個(gè)數(shù)的關(guān)系
二叉樹的存儲(chǔ)結(jié)構(gòu)
(1)二叉樹順序存儲(chǔ)結(jié)構(gòu)
前面提到順序存儲(chǔ)對樹這種一對多的關(guān)系結(jié)構(gòu)實(shí)現(xiàn)起來是比較困難的,但是二叉樹是一種特殊的樹,它的特殊性使得用順序存儲(chǔ)結(jié)構(gòu)也可以實(shí)現(xiàn)。 二叉樹的順序存儲(chǔ)結(jié)構(gòu)就是用一維數(shù)組存儲(chǔ)二叉樹中的結(jié)點(diǎn),并且結(jié)點(diǎn)的存儲(chǔ)位置,也就是數(shù)組的下標(biāo)要能體現(xiàn)結(jié)點(diǎn)之間的邏輯關(guān)系,比如雙親與孩子的關(guān)系、左右兄弟的關(guān)系等。
/*二叉樹順序存儲(chǔ)結(jié)構(gòu),其中數(shù)組下標(biāo)即表示結(jié)點(diǎn)的編號(hào)*/ typedef int SBElemType;/*SBElemType依據(jù)實(shí)際類型而定,這里假設(shè)為int型*/ typedef struct {SBElemType data[MAX_TREE_SIZE];/*結(jié)點(diǎn)數(shù)據(jù)域*/int len;/*數(shù)組長度*/ }SBTree; 1234567(2)二叉鏈表
二叉樹每個(gè)結(jié)點(diǎn)最多有兩個(gè)孩子,所以為它設(shè)計(jì)一個(gè)數(shù)據(jù)域和兩個(gè)指針域是比較自然的想法。我們稱這樣的鏈表叫二叉鏈表,其結(jié)點(diǎn)結(jié)構(gòu)圖如下表: 其中data是數(shù)據(jù)域,lchild和rchild是指針域,分別存放指向左孩子和右孩子的指針。 以下是二叉鏈表結(jié)點(diǎn)結(jié)構(gòu)的定義代碼:
/*二叉樹的結(jié)點(diǎn)結(jié)構(gòu)定義代碼*/ typedef struct BiTNode/*結(jié)點(diǎn)結(jié)構(gòu)*/ {TElemType data;/*結(jié)點(diǎn)數(shù)據(jù)*/struct BiTNode* lchild, *rchild;/*左右孩子指針*/ }BiTNode,*BiTree;結(jié)構(gòu)示意圖如下所示: 就如同樹的存儲(chǔ)結(jié)構(gòu)中討論的一樣,如果有需要,還可以再增加一個(gè)指向其雙親的指針域,那樣就稱之為三叉鏈表。
遍歷二叉樹
(1)二叉樹遍歷原理
對于二叉樹的遍歷來說,次序顯得很重要。 二叉樹的遍歷(traversing binary tree)是指從根結(jié)點(diǎn)出發(fā),按照某種次序依次訪問二叉樹中所有結(jié)點(diǎn),使得每個(gè)結(jié)點(diǎn)被訪問依次且僅被訪問一次。 這里有兩個(gè)關(guān)鍵詞,訪問和次序。 訪問其實(shí)是要根據(jù)實(shí)際的需要來確定具體做什么,比如對每個(gè)結(jié)點(diǎn)進(jìn)行相關(guān)計(jì)算、輸出打印等,它其實(shí)算是一個(gè)抽象操作。在這里我們可以簡單假定就是輸出結(jié)點(diǎn)的數(shù)據(jù)信息。 二叉樹的遍歷次序不同于線性結(jié)構(gòu),最多也是從頭至尾、循環(huán)、雙向等簡單的遍歷方式。樹的結(jié)點(diǎn)之間不存在唯一的前驅(qū)和后繼關(guān)系,在訪問一個(gè)結(jié)點(diǎn)后,下一個(gè)被訪問的結(jié)點(diǎn)面臨著不同的選擇,由于選擇方式的不同,遍歷的次序就完全不同了。
(2)二叉樹遍歷方法
二叉樹的遍歷方式可以有很多,如果我們限制了從左到右的遍歷方式,那么主要就分為四種:
1)前序遍歷
規(guī)則是若二叉樹為空,則空操作返回,否則先訪問根結(jié)點(diǎn),然后前序遍歷左子樹(注意是左子樹而不是左結(jié)點(diǎn)),再前序遍歷右子樹,如下圖,遍歷次序?yàn)?#xff1a;ABDGHCEIF。
?
2)中序遍歷
規(guī)則是若樹為,則空操作返回,否則從根結(jié)點(diǎn)開始(注意并不是先訪問根結(jié)點(diǎn)),中序遍歷根結(jié)點(diǎn)的左子樹,然后是訪問中結(jié)點(diǎn),最后中序遍歷右 子樹。如下圖,遍歷的順序?yàn)?#xff1a;GDHBAEICF。
?
3)后序遍歷
規(guī)則是若樹為空,則空操作返回,否則從左到右先葉子后結(jié)點(diǎn)的方式遍歷 訪問左右子樹,最后是訪問根結(jié)點(diǎn)。如下圖,遍歷次序?yàn)?#xff1a;GHDBIEFCA。
?
4)層次遍歷
規(guī)則是若樹為空,則空操作返回,否則從樹的第一層,也就是根結(jié)點(diǎn)開始訪問,從上而下逐層遍歷 ,在同一層中,按從左到右的順序?qū)Y(jié)點(diǎn)逐個(gè)訪問。如下圖,遍歷順序?yàn)?#xff1a;ABCDEFGHI。
?
我們用圖形的方式來表現(xiàn)樹的結(jié)構(gòu),應(yīng)該說是非常直觀和容易理解,但對于計(jì)算機(jī)來說,它只有循環(huán)、判斷等方式來處理,也就是說,它只會(huì)處理線性序列,而我們剛才提到的四種遍歷方法,其實(shí)都是在把 樹中的結(jié)點(diǎn)變成某種意義的線性序列,這就給程序 的實(shí)現(xiàn)帶來了好處。 另外不同的遍歷提供了 對結(jié)點(diǎn)依次處理的不同方式,可以在遍歷過程中對結(jié)點(diǎn)進(jìn)行各種處理。
(3)前序遍歷算法
二叉樹的定義是用遞歸的方式,所以,實(shí)現(xiàn)遍歷算法也可以采用遞歸,而且及其簡單明了。二叉樹的前序遍歷算法,代碼如下:
/*二叉樹的前序遍歷算法*/ void PreOrderTraverse(BiTree T) {if (NULL == T)/*退出遞歸的條件*/return;printf("%c", T->data);/*顯示結(jié)點(diǎn)數(shù)據(jù),可以改為其他對結(jié)點(diǎn)的操作*/PreOrderTraverse(T->lchild);/*再先遍歷左子樹*/PreOrderTraverse(T->rchild);/*最后遍歷右子樹*/ }對過程6的理解: “訪問了K結(jié)點(diǎn)的右孩子,也是null,返回。于是此函數(shù)執(zhí)行完畢”:“此函數(shù)”是指T指向K結(jié)點(diǎn)時(shí)的函數(shù),“執(zhí)行完畢”是指該結(jié)點(diǎn)不存在左孩子和右孩子,因此不再發(fā)生下一級(jí)遞歸,故已完成該函數(shù)的遞歸。 “返回到上一級(jí)遞歸函數(shù)”:能返回的原因是本級(jí)的遞歸是在上一級(jí)調(diào)用PreOrderTraverse(T->lchild)時(shí)發(fā)生的,這就意味著,上一級(jí)的遞歸還沒完成就已進(jìn)入本級(jí)遞歸,故本級(jí)遞歸完成后,自然要繼續(xù)執(zhí)行上一級(jí)未完成的遞歸。 “也執(zhí)行完畢”:因?yàn)榻Y(jié)點(diǎn)K是結(jié)點(diǎn)H的右孩子,所以結(jié)點(diǎn)K的遞歸完成,也就意味著在結(jié)點(diǎn)H遞歸中已執(zhí)行完P(guān)reOrderTraverse(T->rchild)語句,故結(jié)點(diǎn)H也完成了遞歸。 “返回到打印D時(shí)的函數(shù),調(diào)用PreOrderTraverse(T->rchild)“:”函數(shù)“中 的T指向結(jié)點(diǎn)D;已完成結(jié)點(diǎn)D左子樹的遞歸,繼續(xù)執(zhí)行其右子樹的遞歸。 T指向J時(shí)的函數(shù)執(zhí)行完之后,意味著所有結(jié)點(diǎn)的遞歸都執(zhí)行完,因此完成了該算法的遞歸調(diào)用。
(4)中序遍歷算法
/*二叉樹的中序遍歷遞歸算法*/ void InOrderTraverse(BiTree T) {if (NULL == T)/*遞歸退出的條件*/return;InOrderTraverse(T->lchild);/*先中序遍歷左子樹*/printf("%c", T->data);/*顯示結(jié)點(diǎn)數(shù)據(jù),可以改為其他對結(jié)點(diǎn)的操作*/InOrderTraverse(T->rchild);/*最后中遍歷右子樹*/ }(5)后序遍歷算法
/*二叉樹的后序遍歷遞歸算法*/ void PostOrderTraverse(BiTree T) {if (NULL == T)/*遞歸退出的條件*/return;PostOrderTraverse(T->lchild);/*先中序遍歷左子樹*/PostOrderTraverse(T->rchild);/*再后序遍歷右子樹*/printf("%c", T->data);/*顯示結(jié)點(diǎn)數(shù)據(jù),可以改為其他對結(jié)點(diǎn)的操作*/ }打印后續(xù)結(jié)點(diǎn)的過程:打印K之后,結(jié)點(diǎn)H的遞歸執(zhí)行完,打印H;執(zhí)行上一級(jí),結(jié)點(diǎn)D無右孩子,打印D,D結(jié)點(diǎn)完成遞歸;執(zhí)行上一級(jí),執(zhí)行B結(jié)點(diǎn)的右孩子E遞歸,E無左右孩子,打印E,B的遞歸完成,打印B;執(zhí)行上一級(jí),執(zhí)行A的右孩子C遞歸,C有左孩子F,F有左孩子I,I無左右孩子,打印I;F無右孩子,F遞歸結(jié)束,打印F;返回上一級(jí),C有右孩子G,G無左孩子,有右孩子J,J無左右孩子,打印J;G完成遞歸,打印G;C完成遞歸,打印C;A完成遞歸,打印A。至此,該遞歸算法執(zhí)行結(jié)束。
(6)推導(dǎo)遍歷結(jié)果
本篇大部分是我向其他人學(xué)習(xí)的,如有不會(huì),可以一起探討。網(wǎng)上也有相應(yīng)的課程學(xué)習(xí),比如一個(gè)青島大學(xué)的老師,我就在她那學(xué)習(xí)的。
總結(jié)