1. 程式人生 > >C語言 資料結構 樹和二叉樹

C語言 資料結構 樹和二叉樹

1、樹:是n節點的有限集。樹是n(n=>0)個節點的有限集。 n=0時成為空樹。

在任意一顆非空樹中:(1)有且僅有一個稱為根的節點;(2)當n>0時,其餘節點可分為m(m>0)個互不相交的有限集T1、T2、T3、Tm,其中每個節點又是一棵樹,並且稱為根的子樹。

2 、節點分類:節點擁有的的子樹數稱為節點的度。度為0的稱為終端節點或者葉節點;度不為0的稱為非終端節點或者分支節點樹的度是樹內節點的度的最大值。

3、節點間關係:節點的子樹的根稱為該節點的孩子, 該節點稱為孩子的雙親。同一個雙親的孩子之間稱為兄弟。如果將樹中節點的各子樹看成是從左到右是有次序的,不能互換的,則稱該樹為有序樹,否則稱為無序樹。

4、結點的層次:從根開始起根為第一層,根的孩子為第二層,樹中結點最大層次成為樹的深度或高度。如果樹中結點的各子樹看做是有序的不能互換的則稱該樹為有序樹,否則為無序樹。森林是m(m>=0)顆互不相交的樹的集合。

5、線性結構和樹結構對比:線性結構的第一個元素無前驅,樹結構的根節點無雙親,且唯一。線性表有一個元素無後繼, 樹節點的葉節點無孩子,但可以有多個,線性結構中間元素一個前驅一個後繼,樹節點一個雙親多個孩子。

樹的抽象資料型別定義:

6、樹的儲存結構:

儲存結構:順序儲存、鏈式儲存

問題:用一段連續的儲存空間(陣列)無論如何設計也無法反映樹的邏輯關係。誰是誰的父母?誰是誰的孩子?誰是誰的兄弟?。。。因此,簡單的順序儲存無法表示樹的邏輯關係。

解決辦法:利用順序結構結合鏈式結構的特點!!!

設計思想:雙親表示法、孩子表示法、孩子兄弟表示法。

雙親表示法:以一組連續空間儲存樹的結點,同時每個結點中附設一個指示器指示其雙親結點在陣列中的位置。特點:找根容易O(1)找孩子難。

#define MAX_TREE_SIZE100;

typedef int TElemType;

typedef struct PTNode{

   TElemType data;

   int parent;

}PTNode;

typedef struct{

   PTNode nodes{MAX_TREE_SIZE];

   int r,n;  //根的位置,節點數

}PTree;

找孩子難怎麼辦?。。。增加長子域。如果是2個以上孩子,還得有次子域。。。3個呢。。。

如果節點的孩子數目很多,超過了2個,我們又要關注節點的雙親,又要關注節點的孩子,還要關注節點的兄弟,而且對時間遍歷要求還比較高,那麼我們就可以把此結構拓展設計為有雙親域、孩子域,兄弟域的結構。因此儲存結構的設計是一個非常靈活的過程,一個儲存結構設計是否合理,取決於資料結構的運算是否簡單、方便、時間空間複雜度低。一定要注意原則:適合就好。不是越複雜越好。

孩子表示法思路:把每個節點的孩子節點排列起來,以單鏈表做儲存結構,則n個節點有n個孩子連結串列,如果是葉子節點則單鏈表為空,然後n個頭指標又組成一個線性表,採用順序儲存結構,存放進一個一位陣列中。

找雙親不容易怎麼破?增加雙親域,變成雙親孩子表示法

雙親孩子表示法:是孩子表示法的改進,添加了雙親的儲存結構

孩子兄弟表示法:任意一棵樹,它的節點的第一個孩子如果存在就是唯一的,它的右兄弟如果存在也是唯一的。因此我們設定兩個指標分別指向該節點的第一個孩子和此節點的右兄弟。節點結構

data

firstchild

rightsib

孩子兄弟表示法意義重大:把一顆複雜的樹變成了二叉樹

二叉樹定義二叉樹(Binary Tree)是n(n>=0)個節點的有限集合,該集合或者為空集(稱為空二叉樹),或者由一個根節點和兩顆互不相交的、分別稱為根節點的左子樹、右子樹的二叉樹組成。五種基本形態:空二叉樹、只有一個根節點、根節點只有左子樹、根節點只有右子樹、根節點既有左子樹又有右子樹。

二叉樹的特點

1.每個節點最多有兩棵子樹,即不存在度大於2的節點。

2.左子樹和右子樹是有序的,不能隨便顛倒。

3.即使樹中某個節點只有一棵子樹,也要區分是左子樹還是右子樹。

滿二叉樹:

深度為k且含有2k-1個節點的二叉樹成為滿二叉樹。

完全二叉樹:

如果一個有n個節點的二叉樹按滿二叉樹方式自上而下,自左至右對它進行編號,若樹中所有節點和滿二叉樹的1~n編號完全一致,則稱該樹為完全二叉樹。

錯誤示例:

二叉樹性質:

1.在二叉樹的第i層上最多有2 i-1個節點 。(i>=1)。

2.二叉樹中如果深度為k,那麼最多有2k-1個節點。(k>=1)。

3.n0=n2+1  n0表示度數為0的節點(葉子節點) n2表示度數為2的節點。

4.在完全二叉樹中,具有n個節點的完全二叉樹的深度為[log2n]+1,其中[log2n]+1是向下取整(不大於[log2n]+1的最大整數)。

5.若對含 n 個結點的完全二叉樹從上到下且從左至右進行 1 至 n 的編號,則對完全二叉樹中任意一個編號為 i 的結點:

(1)若 i=1,則該結點是二叉樹的根,無雙親, 否則(i>1時),編號為[i/2] (向下取整)的結點為其雙親結點; 

(2)若 2i>n,則該結點無左孩子,  否則,編號為 2i 的結點為其左孩子結點;

(3)若 2i+1>n,則該結點無右孩子結點,  否則,編號為2i+1 的結點為其右孩子結點。

7、二叉樹的儲存

前面分析已知,樹用順序儲存結構實現是比較困難的,但是二叉樹是一種特殊的樹,因此完全可以用順序儲存結構來描述其關係。

其雙親、孩子、兄弟的關係可以用二叉樹的性質分析得到!!!!!!

順序儲存結構一般只用於完全二叉樹,否則會造成空間浪費。

二叉樹的鏈式儲存結構:二叉連結串列(左右孩子),三叉連結串列(左右孩子帶雙親),線索連結串列。

二叉樹遍歷:從根結點出發,按照某種次序依次訪問二叉樹中的節點,使得每個節點被訪問一次,而且僅被訪問一次。

遍歷次序:前提是限制從左到右:前序遍歷(根左右)、中序遍歷(左根右)、後序遍歷(左右根)、層序遍歷。

前序遍歷(根左右)ABDGHCEIF

中序遍歷(左根右)GDHBAEICF

後序遍歷(左右根)GHDBIEFCA

層序遍歷 ABCDEFGHI

二叉樹遍歷(遞迴演算法):

面試題目:推導遍歷結果。《大話資料結構P212》

二叉樹建立:

前序:AB#D##C##

中序:#B#D#A#C#

後序:###DB##CA

8、線索二叉樹指向前驅或者後繼得指標稱為線索,加上線索的二叉樹連結串列稱為線性連結串列,相應的二叉樹就稱為線索二叉樹。(n個結點的二叉連結串列一共2n個指標域,一共n-1條分支數一共2n-(n-1)=n+1個空指標域。)

9、樹、森林、二叉樹的相互轉化

將樹轉換成二叉樹的步驟是:
(1)加線。就是在所有兄弟結點之間加一條連線;
(2)抹線。就是對樹中的每個結點,只保留他與第一個孩子結點之間的連線,刪除它與其它孩子結點之間的連線;
(3)旋轉。以樹的根結點為軸心,將整棵樹順時針旋轉一定角度,使之結構層次分明。

注意事項:第一個孩子是二叉樹結點的左孩子,兄弟轉換過來的孩子是右孩子!!!

比如FG是右孩子,J是右孩子,而H、I是左孩子

森林轉換為二叉樹:森林是由若干棵樹組成,可以將森林中的每棵樹的根結點看作是兄弟,由於每棵樹都可以轉換為二叉樹,所以森林也可以轉換為二叉樹。將森林轉換為二叉樹的步驟是:
(1)先把每棵樹轉換為二叉樹;
(2)第一棵二叉樹不動,從第二棵二叉樹開始,依次把後一棵二叉樹的根結點作為前一棵二叉樹的根結點的右孩子結點,用線連線起來。當所有的二叉樹連線起來後得到的二叉樹就是由森林轉換得到的二叉樹。

二叉樹轉換為樹:二叉樹轉換為樹是樹轉換為二叉樹的逆過程,其步驟是:
(1)若某結點的左孩子結點存在,將左孩子結點的右孩子結點、右孩子結點的右孩子結點……都作為該結點的孩子結點,將該結點與這些右孩子結點用線連線起來;
(2)刪除原二叉樹中所有結點與其右孩子結點的連線;(3)整理(1)和(2)兩步得到的樹,使之結構層次分明。
二叉樹轉換為森林:二叉樹轉換為森林比較簡單,其步驟如下:
(1)先把每個結點與右孩子結點的連線刪除,得到分離的二叉樹;
(2)把分離後的每棵二叉樹轉換為樹;(3)整理第(2)步得到的樹,使之規範,這樣得到森林。

根據樹與二叉樹的轉換關係以及二叉樹的遍歷定義可以推知,樹的先序遍歷與其轉換的相應的二叉樹的先序遍歷的結果序列相同;樹的後序遍歷與其轉換的二叉樹的中序遍歷的結果序列相同;樹的層序遍歷與其轉換的二叉樹的後序遍歷的結果序列相同。由森林與二叉樹的轉換關係以及森林與二叉樹的遍歷定義可知,森林的先序遍歷和中序遍歷與所轉換得到的二叉樹的先序遍歷和中序遍歷的結果序列相同。

10、哈夫曼樹我們稱判定過程最優的二叉樹為哈夫曼樹,又稱最優二叉樹。

路徑:樹中一個結點到另一個結點之間的分支構成這兩個結點之間的路徑。

路徑長度:路徑上的分枝數目稱作路徑長度。

樹的路徑長度:從樹根到每一個結點的路徑長度之和。
結點的帶權路徑長度:在一棵樹中,如果其結點上附帶有一個權值,通常把該結點的路徑長度與該結點上的權值之積稱為該結點的帶權路徑長度。

樹的帶權路徑長度:如果樹中每個葉子上都帶有一個權值,則把樹中所有葉子的帶權路徑長度之和稱為樹的帶權路徑長度。其中帶權路徑長度最小的二叉樹就稱為哈夫曼樹或最優二叉樹。

哈夫曼樹的構造:

按照構造哈夫曼樹得到的編碼成為哈夫曼編碼。常用於壓縮。