書節上回,我們接著聊二叉樹,N叉樹,以及樹的儲存。
01、滿二叉樹
如果一個二叉樹,除最後一層節點外,每一層的節點數都達到最大值,即每個節點都有兩個子節點,同時所有葉子節點都在最後一層,則這個二叉樹為滿二叉樹。
因此可以得到滿二叉樹有以下性質:
(1)樹的最大層數為k(k>=0,即層數從0開始),則第i層的節點總數為2i,樹的葉子節點總數為2k ,樹的總節點數為2 ^(k+1) - 1;
(2)如果已知數的總節點數為n,則樹的最大層數為k=log2(n+1) - 1(k>=0,即層數從0開始);
(3)從樹的頂層開始從上到下,從左到右,對每個節點按順序進行編號,根節點為1作為起始;
a.對於節點i,如果其存在父節點,則父節點編號為i/2向下取整;
b.對於節點i,如果其存在左節點則左節點編號為2i,如果其存在右節點則右節點編號為2i+1;
02、完全二叉樹
如果一棵樹,葉子節點只能出現在最後兩層,並且最後一層的節點都集中在左側且從左到右是連續的。
由此我們可以得到完全二叉樹有以下性質:
(1)葉子節點只能出現在倒數第一層和倒數第二層;
(2)倒數第一層的所有葉子節點都集中此層最左側連續位置;
(3)倒數第二層如果存在葉子節點則層的所有葉子節點都集中在此層最右側連續位置;
(4)所有節點中如果存在一個節點只有一個子節點,則此節點一定在倒數第二層,並且這個子節點一定是左節點;
(5)滿二叉樹一種特殊的完全二叉樹;
(6)滿二叉樹性質(3)也適用於完全二叉樹;
(7)如果完全二叉樹不是滿二叉樹,則除最後一層外為滿二叉樹,適用滿二叉樹所有性質;
(8)如果節點總數為n,並且i<=n/2,則第i個節點為非葉子節點;
(9)如果節點總數為n,如果n為偶數則葉子節點數為n/2;如果n為奇數則葉子節點數為(n+1)/2;
(10)如果樹的層數為k(k>=0),則樹數的總節點數的訪問為[2k,2(k+1) - 1)];
(11)如果已知數的總節點數為n,則樹的最大層數為k=log2(n+1) (k>=0,即層數從0開始),並且k向下取整;
03、二叉搜尋樹
二叉搜尋樹是一種特殊的二叉樹,又叫二叉查詢樹、二叉排序樹,可以說這是一種為了查詢而生的二叉樹。
二叉搜尋樹有以下性質:
(1)每個節點值必須唯一,不能有重複值;
(2)每個節點最多有兩個子節點;
(3)對於任意一個節點,如果其存在左子樹則左子樹上所有節點值小於該節點值,如果其存在右子樹則右子樹上所有節點值大於該節點值;
(3)左子樹和右子樹本身也各自是二叉搜尋樹;
04、平衡二叉樹
平衡二叉樹顧名思義就是使二叉樹平衡在某種狀態下,某種狀態具體指樹中任意一個節點左右子樹高度差絕對值小於等於1,並且其左右子樹同樣也是平衡二叉樹。
平衡二叉樹是透過控制樹的深度來最佳化二叉搜尋樹平均操作時間。
AVL樹是平衡二叉樹的一種特例,嚴格執行平衡二叉樹的定義,也是最早發明的自平衡二叉搜尋樹。
紅黑樹也是一種自平衡二叉搜尋樹,但其並沒有嚴格執行平衡二叉樹定義,平衡條件相對比較寬鬆,允許左右子樹高度相差絕對值大於1。
這兩種樹我們後面會找機會單獨詳細講解。
此外還有哈夫曼樹、線段樹、伸展樹、替罪羊樹等二叉樹這裡就不一一介紹了,後面我們單獨拿出來講解。
05、N叉樹
B樹、B+樹、2-3-4樹、R樹、Trie樹等多叉樹我們後面找機會單獨拿出來詳解。
06、儲存結構
1、順序儲存
順序儲存只用一組連續的地址空間儲存整個樹。
當然並不是所有樹都適合順序儲存的,還記得上面說的滿二叉樹的性質(3)嗎?如果把滿二叉樹從上到下、從左到右,則左右子節點編號可以透過父節點編號表示。如果父節點編號為i,則其左子節點編號為2i,其右子節點編號為2i+1。而根節點作為已知起始值1,就意味著其後代節點都可以透過根節點直接或間接表示。
而編號不經讓我們想到陣列下標,這就意味著我們可以把整個滿二叉樹裝進陣列裡。因為完全二叉樹也滿足滿二叉樹的性質(3),所以完全二叉樹也可以裝進陣列。如下圖。
那如果非完全二叉樹呢?能否放入陣列中呢,答案是肯定可以的。我們只需要把非完全二叉樹想象成滿二叉樹,把缺少的節點虛擬補全,然後對其編號,最後裝入陣列,入下圖:
但是我們會發現編號4、5、7位都為空值,當然編號7是可以去掉的,即使如此,還是很浪費陣列空間的,因此順序儲存是比較適合完全二叉樹儲存的,而其他型別二叉樹並不是很適合。
2、鏈式儲存
順序儲存有其侷限性,因此大多數樹都是使用鏈式儲存。鏈式儲存的核心思想就是每一個節點設計為兩部分,其一為資料域存放元素值,其二為指標域存放指向子節點指標,節點有多少個子節點指標域就有多少個指標。
我們還是以二叉樹為例,鏈式儲存結構如下:
注:測試方法程式碼以及示例原始碼都已經上傳至程式碼庫,有興趣的可以看看。https://gitee.com/hugogoos/Planner