完全二叉樹


完全二叉樹

完全二叉樹是一種特殊的二叉樹,滿足以下要求:

  1. 所有葉子節點都出現在 k 或者 k-1 層,而且從 1 到 k-1 層必須達到最大節點數;

  2. 第 k 層可以不是滿的,但是第 k 層的所有節點必須集中在最左邊。 
    需要注意的是不要把完全二叉樹和“滿二叉樹”搞混了,完全二叉樹不要求所有樹都有左右子樹,但它要求:

  3. 任何一個節點不能只有右子樹沒有左子樹

  4. 葉子節點出現在最后一層或者倒數第二層,不能再往上

用一張圖對比下“完全二叉樹”和“滿二叉樹”:

當我們用數組實現一個完全二叉樹時,葉子節點可以按從上到下、從左到右的順序依次添加到數組中,然后知道一個節點的位置,就可以輕松地算出它的父節點孩子節點的位置。

以上面圖中完全二叉樹為例,標號為 2 的節點,它在數組中的位置也是 2,它的父節點就是 (k/2 = 1),它的孩子節點分別是 (2k=4) 和 (2k+1=5),別的節點也是類似。

完全二叉樹使用場景:

根據前面的學習,我們了解到完全二叉樹的特點是:“葉子節點的位置比較規律”。因此在對數據進行排序或者查找時可以用到它,比如堆排序就使用了它,后面學到了再詳細介紹。

二叉查找樹

二叉樹的提出其實主要就是為了提高查找效率,比如我們常用的 HashMap 在處理哈希沖突嚴重時,拉鏈過長導致查找效率降低,就引入了紅黑樹。

我們知道,二分查找可以縮短查找的時間,但是它要求 查找的數據必須是有序的。每次查找、操作時都要維護一個有序的數據集,於是有了二叉查找樹這個概念。

二叉查找樹(又叫二叉排序樹),它是具有下列性質的二叉樹:

  1. 若左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;

  2. 若右子樹不空,則右子樹上所有結點的值均大於或等於它的根結點的值;

  3. 左、右子樹也分別為二叉排序樹。

如下圖所示: 

 

也就是說,二叉查找樹中,左子樹都比節點小,右子樹都比節點大,遞歸定義

根據二叉排序樹這個特點我們可以知道:二叉排序樹的中序遍歷一定是從小到大的

比如上圖,中序遍歷結果是:

1 3 4 6 7 8 10 13 14

二叉排序樹的性能

在最好的情況下,二叉排序樹的查找效率比較高,是 O(logn),其訪問性能近似於折半查找;

但最差時候會是 O(n),比如插入的元素是有序的,生成的二叉排序樹就是一個鏈表,這種情況下,需要遍歷全部元素才行(見下圖 b)。

 

如果我們可以保證二叉排序樹不出現上面提到的極端情況(插入的元素是有序的,導致變成一個鏈表),就可以保證很高的效率了。

但這在插入有序的元素時不太好控制,按二叉排序樹的定義,我們無法判斷當前的樹是否需要調整。

因此就要用到平衡二叉樹(AVL 樹)了。

平衡二叉樹

平衡二叉樹的提出就是為了保證樹不至於太傾斜,盡量保證兩邊平衡。因此它的定義如下:

  1. 平衡二叉樹要么是一棵空樹

  2. 要么保證左右子樹的高度之差不大於 1

  3. 子樹也必須是一顆平衡二叉樹

也就是說,樹的兩個左子樹的高度差別不會太大。

那我們接着看前面的極端情況的二叉排序樹,現在用它來構造一棵平衡二叉樹。

以 12 為根節點,當添加 24 為它的右子樹后,根節點的左右子樹高度差為 1,這時還算平衡,這時再添加一個元素 28:

這時根節點 12 覺得不平衡了,我左孩子一個都沒有,右邊都有倆了,超過了之前說的最大為 1,不行,給我調整!

於是我們就需要調整當前的樹結構,讓它進行旋轉。

因為最后一個節點加到了右子樹的右子樹,就要想辦法給右子樹的左子樹加點料,因此需要逆時針旋轉,將 24 變成根節點,12 右旋成 24 的左子樹,就變成了這樣(有點丑哈哈):

這時又恢復了平衡,再添加 37 到 28 的右子樹,還算平衡:

這時如果再添加一個 30,它就需要在 37 的左子樹:

這時我們可以看到這個樹又不平衡了,以 24 為根節點的樹,明顯右邊太重,左邊太稀,想要保持平衡就 24 得讓位給 28,然后變成這樣:

丑了點,但的確保持了平衡。

依次類推,平衡二叉樹在添加和刪除時需要進行旋轉保持整個樹的平衡,內部做了這么復雜的工作后,我們在使用它時,插入、查找的時間復雜度都是 O(logn),性能已經相當好了。

總結

閱讀完這篇文章,相信你對“完全二叉樹、二叉查找樹和平衡二叉樹”這三個二叉樹有了更加深刻的認識,有了這個基礎,再去看 B+ B- 紅黑樹,就相對容易些了,后面有機會的話我再寫兩篇這種的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM