資料結構系列 - 二叉樹
1. 前言
二叉樹是樹形結構的一個重要型別。許多實際問題抽象出來的資料結構往往是二叉樹的形式,即使是一般的樹也能簡單地轉換為二叉樹,而且二叉樹的儲存結構及其演算法都較為簡單,因此二叉樹顯得特別重要。
2. 二叉樹的定義
二叉樹(BinaryTree)是n(n≥0)個結點的有限集,它或者是空集(n=0),或者由一個根結點及兩棵互不相交的、分別稱作這個根的左子樹和右子樹的二叉樹組成。
二叉樹可以是空集;根可以有空的左子樹或右子樹;或者左、右子樹皆為空。
二叉樹的五種基本形態如下圖所示。
圖 二叉樹的五種基本形態
3. 二叉樹的基本操作
(1)InitBiTree(&T);
操作結果:構造空二叉樹T。
(2)DestroyBiTree(&T);
初始條件:二叉樹T存在。
操作結果:銷燬二叉樹T。
(3)CreateBiTree(&T,definition);
初始條件:definition給出二叉樹T的定義。
操作結果:按definition構造二叉樹。
(4)ClearBiTree(&T);
初始條件:二叉樹T存在。
操作結果:將二叉樹T清為空樹。
(5)BiTreeEmpty(T);
初始條件:二叉樹T存在。
操作結果:若T為空二叉樹,則返回TRUE,否則返回FALSE。
(6)BiTreeDepth(T);
初始條件:二叉樹T存在。
操作結果:返回二叉樹T的深度。
(7)Root(T);
初始條件:二叉樹T存在。
操作結果:返回二叉樹T的根。
(8)Value(T,e);
初始條件:二叉樹T存在,e是T中某個結點。
操作結果:返回e的值。
(9)Assign(T,&e,value);
初始條件:二叉樹T存在,e是T中某個結點。
操作結果:結點e賦值為value。
(10)Parent(T,e);
初始條件:二叉樹T存在,e是T中某個結點。
操作結果:若e是T的非根結點,則返回它的雙親,否則返回"空"。
(11)LeftChild(T,e);
初始條件:二叉樹T存在,e是T中某個結點。
操作結果:返回e的左子女。若e無左子女,則返回"空"。
(12)RightChild(T,e);
初始條件:二叉樹T存在,e是T中某個結點。
操作結果:返回e的右子女。若e無右子女,則返回"空"。
(13)InsertChild(T,p,LR,c);
初始條件:二叉樹T存在,p指向T中某個結點,LR為0或1,非空二叉樹c與T不相交且右子樹為空。
操作結果:根據LR為0或1,插入c為T中p所指結點的左或右子樹。P所指結點的原有左或右子樹則成為c的右子樹。
(14)DeleteChild(T,p,LR);
初始條件:二叉樹T存在,p指向T中某個結點,LR為0或1。
操作結果:根據LR為0或1,刪除T中p所指結點的左或右子樹。
(15)PreOrderTraverse(T,Visit());
初始條件:二叉樹T存在,Visit是對結點操作的應用函式。
操作結果:先序遍歷T,對每個結點呼叫函式Visit一次且僅一次。一旦Visit()失敗,則操作失敗。
(16)InOrderTraverse(T,Visit());
初始條件:二叉樹T存在,Visit是對結點操作的應用函式。
操作結果:中序遍歷T,對每個結點呼叫函式Visit一次且僅一次。一旦Visit()失敗,則操作失敗。
(17)PostOrderTraverse(T,Visit());
初始條件:二叉樹T存在,Visit是對結點操作的應用函式。
操作結果:後序遍歷T,對每個結點呼叫函式Visit一次且僅一次。一旦Visit()失敗,則操作失敗。
4. 幾種特殊的二叉樹
(1)滿二叉樹(FullBinaryTree)
一棵高度為k,並且含有 2 k -1 個結點的二叉樹為滿二叉樹。因此,在滿二叉樹中,每一層結點都達到了最大個數。除最低層結點的度為0外,其他各層結點的度都為2。
滿二叉樹的特點:
- 每一層上的結點數都達到最大值。即對給定的高度,它是具有最多結點數的二叉樹。
- 滿二叉樹中不存在度數為1的結點,每個分支結點均有兩棵高度相同的子樹,且樹葉都在最下一層上。
(2)完全二叉樹(Complete BinaryTree)
若一棵二叉樹至多隻有最下面的兩層上結點的度數可以小於2,並且最下一層上的結點都集中在該層最左邊的若干位置上,則此二叉樹稱為完全二叉樹。
完全二叉樹的特點:
- 滿二叉樹是完全二叉樹,完全二叉樹不一定是滿二叉樹。
- 在滿二叉樹的最下一層上,從最右邊開始連續刪去若干結點後得到的二叉樹仍然是一棵完全二叉樹。
- 在完全二叉樹中,若某個結點沒有左孩子,則它一定沒有右孩子,即該結點必是葉結點。
圖 特殊形態的二叉樹
5. 二叉樹的性質
性質1:二叉樹第i層上的結點數目最多為2 i-1 (i≥1)。
性質2:深度為k的二叉樹至多有2 k -1個結點(k≥1)。
性質3:在任意-棵二叉樹中,若終端結點的個數為n 0 ,度為2的結點數為n 2 ,則n o =n 2 +1。
性質4:具有n個結點的完全二叉樹的深度為: Line"/>
6. 二叉樹的儲存表示與實現
二叉樹的儲存結構有兩種:順序儲存表示和鏈式儲存表示。
(1)順序儲存表示
按照順序儲存結構的定義,用一組地址連續的儲存單元以此自上而下、自左至右儲存完全二叉樹上的結點元素,即將完全二叉樹上編號為i的結點元素儲存在如上定義的一維陣列中下標為i-1的分量中。對於一般二叉樹,則應將其每個結點與完全二叉樹上的結點相對照,儲存在一維陣列的相應分量中,空缺表示不存在此結點。由此可見,這種順序儲存結構比較適用於完全二叉樹,而不太適用於非完全二叉樹。因為,在最壞的情況下,一個深度為k且只有k個節點的單支樹(樹中不存在度為2的結點)卻需要長度為2k-1的一維陣列。如下圖所示:
圖 完全二叉樹的順序儲存表示
圖 一般二叉樹的順序儲存
二叉樹的順序儲存表示:
#define MAX_TREE_SIZE 100 //二叉樹的最大結點數 typedef ElemType SqBiTree[MAX_TREE_SIZE] //0號單元儲存根結點 SqBiTree bt;
二叉樹順序儲存的優缺點:
① 對完全二叉樹而言,順序儲存結構既簡單又節省儲存空間。
② 一般的二叉樹採用順序儲存結構時,雖然簡單,但易造成儲存空間的浪費。【例】最壞的情況下,一個深度為k且只有k個結點的右單支樹需要2k-1個結點的儲存空間。
③ 在對順序儲存的二叉樹做插入和刪除結點操作時,要大量移動結點。
(2)鏈式儲存表示
二叉樹的每個結點最多有兩個孩子。用連結方式儲存二叉樹時,每個結點除了儲存結點本身的資料外,還應設定兩個指標域lchild和rchild,分別指向該結點的左孩子和右孩子。結點的結構為:
圖 二叉樹鏈式儲存結構
其中,data表示值域,用於儲存對應的資料元素,lchild和rchild分別表示左指標域和右指標域,分別用於儲存左子女結點和右子女結點(即左、右子樹的根結點)的儲存位置(即指標)。
對應C語言結點型別定義如下:
typedef char DataType; //使用者可根據具體應用定義DataType的實際型別 typedef struct node{ DataType data; Struct node *lchild,*rchild; //左右孩子指標 }BinTNode; //結點型別 typedef BinTNode *BinTree;//BinTree為指向BinTNode型別結點的指標型別
在一棵二叉樹中,所有型別為BinTNode的結點,再加上一個指向開始結點(即根結點)的BinTree型頭指標(即根指標)root,就構成了二叉樹的鏈式儲存結構,並將其稱為二叉連結串列。
下面左圖所示二叉樹的二叉連結串列如下面中圖所示:
圖 二叉樹鏈式儲存結構示意圖
注意:
① 一個二叉連結串列由根指標root惟一確定。若二叉樹為空,則root=NULL;若結點的某個孩子不存在,則相應的指標為空。
② 具有n個結點的二叉連結串列中,共有2n個指標域。其中只有n-1個用來指示結點的左、右孩子,其餘的n+1個指標域為空。
經常要在二叉樹中尋找某結點的雙親時,可在每個結點上再加一個指向其雙親的指標parent,形成一個帶雙親指標的二叉連結串列。【例】上面右圖是上面左圖所示的二叉樹的帶雙親指標的二叉連結串列。