算法世界中,樹結構是較大的一塊知識體系,從二叉樹,到B樹,到紅黑樹,赫夫曼樹等耳熟能詳的算法都可以歸納到“樹”這一體系。本文先只對樹以及二叉樹的基本概念進行闡述,更多算法的講解請參閱後續博文。
1. 樹
樹是一種數據結構,它是由n(n>=1)個有限結點組成一個具有層次關係的集合。把它叫做“樹”是因爲它看起來像一棵倒掛的樹。一顆簡單的樹如下:
數的定義很簡單,但是有兩點需要強調:
- N>0時:只能有一個根節點,不能有多個根節點
- 子樹的個數沒有限制,但是子樹之間一定不能相交,下面的數就是一顆不合法的樹:
1.1 樹的相關名詞
- 節點的度:一個節點含有的子樹的個數稱爲該節點的度;
- 葉節點或終端節點:度爲0的節點稱爲葉節點;
- 非終端節點或分支節點:度不爲0的節點;
- 雙親節點或父節點:若一個節點含有子節點,則這個節點稱爲其子節點的父節點;
- 孩子節點或子節點:一個節點含有的子樹的根節點稱爲該節點的子節點;
- 兄弟節點:具有相同父節點的節點互稱爲兄弟節點;
- 樹的度:一棵樹中,最大的節點的度稱爲樹的度;
- 節點的層次:從根開始定義起,根爲第1層,根的子節點爲第2層,以此類推;
- 樹的高度或深度:樹中節點的最大層次;
- 堂兄弟節點:雙親在同一層的節點互爲堂兄弟;
- 節點的祖先:從根到該節點所經分支上的所有節點;
- 子孫:以某節點爲根的子樹中任一節點都稱爲該節點的子孫。
- 森林:由m(m>=0)棵互不相交的樹的集合稱爲森林;
1.2 樹的存儲結構
通過結合順序以及鏈式存儲,可以實現對樹結構的存儲。主要有三種存儲方式:(1)雙親表示法 (2)雙親孩子表示法(3)孩子兄弟表示法
1.2.1 雙親表示法
對於雙親表示法,除了需要知道節點本身的data信息,還需要着重關注該節點的父節是誰,最後可以根據需要來調整存儲的結構。
例如,要存儲下面的樹:
- 如果只關心parent,則可以採用下面的結構進行順序存儲
這樣的存儲結構,可以很容易的根據parent列的值找到對應的parent(時間複雜度爲O(1)),但想要知道某個節點的有哪些子節點,則需要遍歷整個樹
-
在存了parent的信息後,還可以增加該節點的長子信息(該節點最左邊的子節點)
-
如果使用的場景不關心長子的信息,而是關心又兄弟的話,則可以採用下面的存儲結構
-
從前面幾個例子可以看出,除了parent是必須的,其他的信息如果你需要都可以對結構進行擴展。
1.2.2 雙親孩子表示法
對於基本的雙親表示法,想要找到parent很容易,但是如果有多個子節點,想要找到所有的子節點就不是很方便,如何才能做到了?看看下面的順序+鏈表的存儲結構:
上面的結構有以下優點:
- 很容易找到parent節點
- 很容易找到所有的子節點
1.2.3 孩子兄弟表示法
通過名字可以看出,該表示法關心的是孩子和兄弟,具體是哪個孩子哪個兄弟了?
- 孩子:最左側的孩子
- 兄弟:該節點右邊的兄弟
節點的結構爲:節點本身|最左側孩子節點|右邊的兄弟,最終表示的結果如下:
該結構由於沒存parent的信息,因爲查找某個節點的parent的比較麻煩,但是可以將該結構增加一個parent信息。
如果仔細觀察該結構的節點,會發現任何一個節點現在最多隻有兩個節點了,一不小心居然將一顆複雜的樹轉化成了二叉樹。二叉樹是啥?往後看!
2. 二叉樹
如果一棵樹每個結點最多有兩個子樹,那麼這課樹就是二叉樹。
- 每個節點最多有兩個子樹
- 樹的結點無左、右之分,而二叉樹的結點有左、右之分
- 即使只有一顆子樹,也要區分左右
2.1 二叉樹的性質
- 在非空二叉樹中,第i層的結點總數不超過2^(i-1);
- 深度爲h的二叉樹最多有2^h-1 個結點(h>=1),最少有h個結點;
- 對於任意一棵二叉樹,如果其葉結點數爲N0,而度數爲2的結點總數爲N2,則N0=N2+1;
- 具有n個結點的完全二叉樹的深度爲|Log_2^N|+1 (注:[ ]表示向下取整)
- 對於一顆有n個節點的完全二叉樹的節點按照層次編號,對於任一節點i
- 如果i=1,則節點i是根,如果i>1,則父節點是i/2
- 如果2i>n,則節點無左孩子,否則其左孩子是節點2i
- 如果2i+1>n,則節點i無右孩子,否則其右孩子是2i+1
2.2 二叉樹的分類
- 滿二叉樹:除了葉結點外每一個結點都有左右子葉且葉子結點都處在最底層的二叉樹。
- 完全二叉樹:若設二叉樹的高度爲h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第h層有葉子結點,並且葉子結點都是從左到右依次排布,這就是完全二叉樹。
完全二叉樹的例子如下:
- 平衡二叉樹:平衡二叉樹又被稱爲AVL樹(區別於AVL算法),它是一棵二叉排序樹,且具有以下性質:它是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,並且左右兩個子樹都是一棵平衡二叉樹。
- 斜樹:所有節點只有左節點或是右節點的樹。
2.3 二叉樹的存儲
二叉樹的存儲方式可以用前面介紹的樹的存儲方式,但由於二叉樹每個節點最多有兩個孩子,可以通過鏈表的方式來存儲二叉樹。
如有需要,可以在結構體中放一個parent的指針。
2.4 二叉樹的遍歷
根據不同的遍歷順序,二叉樹的遍歷方式可以分爲以下幾種:
- 先序遍歷:根->左子樹->右子樹(先序)
- 中序遍歷:左子樹->根->右子樹(中序)
- 後序遍歷:左子樹->右子樹->根(後序)
- 層次遍歷:一層一層的遍歷
3. 樹,森林,二叉樹的轉換
3.1 樹轉爲二叉樹
前文提到的孩子兄弟表示法是一種方式,還可以通過如下方式進行轉換
- 在所有兄弟之間加一條連線
- 只保留每個節點與第一個子節點的連線,與其他子節點的連線都刪掉
- 以根節點爲中心點,將樹順時針旋轉一定的角度,使層次分明。
3.2 森林轉爲二叉樹
森林轉爲二叉樹的步驟如下:
- 把每顆樹轉爲二叉樹
- 第一顆樹不懂,從第二顆開始,依次把後一顆二叉樹的根節點左右前一顆二叉樹的右孩子。
3.3 二叉樹轉爲樹
是樹轉換爲二叉樹的逆過程。
1.加線。若某結點X的左孩子結點存在,則將這個左孩子的右孩子結點、右孩子的右孩子結點、右孩子的右孩子的右孩子結點…,都作爲結點X的孩子。將結點X與這些右孩子結點用線連接起來。
2.去線。刪除原二叉樹中所有結點與其右孩子結點的連線。
3.4 二叉樹轉爲森林
假如一棵二叉樹的根節點有右孩子,則這棵二叉樹能夠轉換爲森林,否則將轉換爲一棵樹。
1.從根節點開始,若右孩子存在,則把與右孩子結點的連線刪除。再查看分離後的二叉樹,若其根節點的右孩子存在,則連線刪除…。直到所有這些根節點與右孩子的連線都刪除爲止。
2.將每棵分離後的二叉樹轉換爲樹。
如下圖所示: