一、樹的定義
樹是由n(n>=1)個有限結點組成一個具有層次關係的集合,根朝上,葉朝下。
樹具有以下的特點:
① 每個結點有零個或多個子結點;沒有父結點的結點稱爲根結點;
② 每一個非根結點有且只有一個父結點;
③ 除了根結點外,每個子結點可以分爲多個不相交的子樹。
注意:
① n > 0時根結點是唯一的,不可能存在多個根節點;
② 由子結點構成的子樹個數沒有限制,但它們一定是互不相交的。
二、樹的相關概念
- 空樹: 樹結點的個數(n≥0),當n=0時,稱爲空樹;
- 結點間的關係:
① 結點的子樹的根稱爲該結點的孩子(Child),相應地,該結點稱爲孩子的雙親(Parent);
② 同一個雙親的孩子之間互稱兄弟;
③ 結點的祖先是從根到該結點所經分支上的所有結點;
④ 反之,以某結點爲根的子樹中的任一結點都稱爲該結點的子孫。
- 結點的分類:
① 結點的度(Degree): 結點擁有的子樹數;
② 樹的度: 樹內各結點的度的最大值;
③ 葉結點: 度爲0的結點,也稱爲終端結點,無孩子,在樹中可以存在多個;
④ 分支結點: 度不爲0的結點,也稱爲非終端結點;
⑤ 內部結點: 樹中除根結點之外的分支結點,一個雙親多個孩子;
- ① 結點的層次: 由結點位置,從根開始定義,根爲第一層,往下遞增(同層的結點互爲堂兄弟);
② 樹的深度(Depth): 樹中結點的最大層次,也稱爲高度。
三、樹的存儲結構
-
雙親表示法:
每個結點分別由一個數據域和一個指針域構成。數據域存儲結點的數據信息,指針域儲存雙親的地址。
注意: 因爲根結點沒有雙親,故約定其指針域爲NULL。 -
孩子表示法:
每個結點分別由一個數據域和多個指針域構成。指針域指向每一棵子樹的根結點。也稱爲多重鏈表表示法。
注意: 因爲不同結點的孩子個數可能有差異,故需要設計方案解決:
① 按樹的度,也就是樹中各個結點度的最大值,指定指針域的個數;(浪費內存)
② 每個結點指針域的個數等於該結點的度。增添一個專門位置,存儲結點指針域的個數;(維護麻煩)
③ 將每個結點的孩子排列起來,以單鏈表作存儲結構(葉子結點則此鏈表爲空)。然後將這些鏈表的頭指針,組成一個線性表存儲。 -
孩子兄弟表示法:
每個結點分別由一個數據域和兩個指針域構成。指針域指向該結點的第一個孩子和右兄弟。該方法本質上等價於將任意一個複雜的樹,轉換成二叉樹結構。
注意: 若某結點沒有孩子或右兄弟,將對應的指針域設定爲NULL。
四、特殊樹——二叉樹
4.1 二叉樹的定義
二叉樹是n個結點的有限集合,該集合或者爲空集(稱爲空二叉樹),或者由一個根結點和兩棵互不相交的(分別稱爲根結點的左子樹、右子樹)二叉樹組成。
二叉樹具有以下特點:
① 每個結點最多隻有兩棵子樹,所以二叉樹中的結點度取值只能爲三個值——0、1、2;
② 左子樹和右子樹是有順序的,不能任意顛倒;
③ 即使樹中某結點只有一棵子樹,也要區分其是左子樹還是右子樹。
二叉樹具有五種基本形態:
① 空二叉樹;
② 只有一個根結點;
③ 根結點只有左子樹;
④ 根結點只有右子樹;
⑤ 根結點既有左子樹又有右子樹。
4.2 二叉樹的存儲結構
- 順序存儲結構: 用一維數組存儲二叉樹中的結點,並且結點的存儲位置(數組的下標),需要體現結點之間的邏輯關係(雙親與孩子、兄弟等)。(維護麻煩、浪費內存,一般不會使用)
存儲時,將目標二叉樹看作完全二叉樹,對各結點進行層序編號。其編號,就是在數組中存儲時的下標。若某個編號對應的結點不存在,在數組中存儲特殊標誌值表示。 - 二叉鏈表: 每個結點分別由一個數據域和兩個指針域構成。指針域分別指向存放左、右孩子的地址。如下圖:
4.3 二叉樹的性質
- 在二叉樹的第i層上至多有2(i - 1) 個結點(i ≥ 1).;
- 深度爲k的二叉樹至多有2k - 1個結點(k ≥ 1) ;
- 對任何一棵二叉樹,若終端結點數爲a,度爲2的結點數爲b,則a = b + 1;
- 具有n個結點的完全二叉樹的深度爲 [log2n] + 1([x]表示不大於x的最大整數);
- 如果對一棵有n個結點的完全二叉樹(其深度爲[log2n] + 1)的結點按層序從上至下、從左至右編號,對任一結點i(1 ≤ i ≤ n)有:
① 若i = 1,則結點i是二叉樹的根,無雙親;若i > 1,其雙親是結點 [i / 2];
② 若2i > n,則結點i無左孩子(結點i爲葉子結點);否則,其左孩子是結點2i;
③ 若2i + 1 > n,則結點i無右孩子;否則,其右孩子是結點2i + 1。
4.4 特殊形式二叉樹
4.4.1 斜樹:
所有結點都只有左子樹的二叉樹叫左斜樹。所有結點都只有右子樹的二叉樹叫右斜樹。這兩者統稱爲斜樹。
4.4.2 滿二叉樹:
在一棵二叉樹中,若所有分支結點都存在左子樹和右子樹,並且所有葉子都在同一層上,這樣的二叉樹稱爲滿二叉樹。
滿二叉樹具有以下特點:
① 葉子只能出現在最下一層,出現在其它層就不可能達到平衡;
② 非葉子結點的度一定是2;
③ 在同樣深度的二叉樹中,滿二叉樹的結點個數最多,葉子樹最多。
4.4.3 完全二叉樹:
對一個二叉樹的結點按層次從上至下、從左至右編號。如果每個編號,與等深度的滿二叉樹相同位置的結點完全一致。則稱該樹爲完全二叉樹。
完全二叉樹具有以下特點:
① 葉子結點只能出現在最下兩層;
② 最下層的葉子一定集中在左部連續位置;
③ 倒數二層,若有葉子結點,一定都在右部連續位置;
④ 如果結點度爲1,則該結點只有左孩子,既不存在只有右子樹的情況;
⑤ 同樣結點數的二叉樹,完全二叉樹的深度最小。
4.4.4 線索二叉樹:
利用二叉樹結點的空指針域,存儲該結點以某種次序遍歷的前驅、後繼(線索),提高遍歷效率的同時,增強了內存空間的利用效率。加了線索的二叉鏈表稱爲線索鏈表,相應的二叉樹稱爲線索二叉樹。而以某種次序遍歷,使一個二叉樹變成線索二叉樹的過程,稱爲線索化。
線索二叉樹的結點變量,需要包含兩個成員標誌(ltag、rtag),來表示lchild、rchild是指向左右孩子,還是前驅後繼。如下:
4.5 二叉樹的遍歷
二叉樹的遍歷是指從根結點出發,按照某種次序依次訪問二叉樹中所有結點,使得每個結點被訪問一次且僅被訪問一次。
- 前序遍歷: 若二叉樹爲空,則空操作返回;否則先訪問根結點,然後前序遍歷左子樹,再前序遍歷右子樹(根——左子樹——右子樹)。如下:
- 中序遍歷: 若二叉樹爲空,則空操作返回;否則先遍歷根結點的左子樹,然後訪問根結點,再中序遍歷右子樹(左子樹——根——右子樹)。如下:
- 後序遍歷: 若二叉樹爲空,則空操作返回;否則按從左到右、先葉子後結點的方式遍歷訪問左右子樹,最後訪問根結點(左子樹——右子樹——根)。如下:
- 層序遍歷: 若二叉樹爲空,則空操作返回;否則從樹的第一層,即根結點開始,按從上而下、從左到右的順序逐個訪問結點。如下:
五、樹、森林、二叉樹的轉換
5.1 “樹”轉換成“二叉樹”
① 加線。在所有兄弟結點之間加一條連線;
② 去線。對樹中每個結點,只保留它與第一個孩子結點的連線,刪除它與其他孩子結點之間的連線;
③ 層次調整。以樹的根結點爲軸心,講整棵樹順時針旋轉一定角度,使其結構層次分明。注意第一個孩子是二叉樹結點的左孩子,兄弟轉換過來的孩子是結點的右孩子。
5.2 “森林”轉換成“二叉樹”
① 把每棵樹轉換爲二叉樹;
② 第一棵二叉樹不動。從第二棵二叉樹開始,依次把後一棵二叉樹的根結點,作爲前一棵二叉樹的根結點的右孩子,用線連接起來。當所有的二叉樹連接起來後,就得到了由森林轉換而來的二叉樹。
5.3 “二叉樹”轉換成“樹”
① 加線。若某結點A,存在左孩子結點B,則將B的所有右孩子結點,都作爲A的孩子,用線連接起來;
② 去線,刪除原二叉樹中所有結點與其右孩子結點的連線;
③ 層次調整。使之結構層次分明。
如下:
5.4 “二叉樹”轉換成“森林”
① 從根結點開始,若右孩子存在,則把與右孩子結點相連的線刪除。再查看分離後的二叉樹,若右孩子存在,則連線刪除……,直到所有右孩子連線都刪除爲止;
② 將每棵分離後的二叉樹轉換成樹即可。