數據結構與算法7:哈夫曼樹,二叉查找樹,平衡二叉查找樹,B樹,B+樹

    這篇文章討論一些常用的樹結構和它們的一點相關性質和算法。

    1. 哈弗曼樹
    哈弗曼樹的主要目的是壓縮。比如對一系列頻率不同的字符,分別給它們不同長度而沒有歧義的編碼,使得期望的總編碼長度降到最低。哈弗曼樹比較好理解,不多說,具體過程大概是先選擇頻率最低的兩個字符,分別作爲樹的最底層兩個節點,然後給它們一個父節點,父節點的頻率記爲它們的合,考察當前剩餘的節點和當前沒有父節點的樹中節點,找出兩個最低的,再重複上面的過程合併。直到每個字符都被放進樹中,此時節點構成一顆二叉樹,除根節點外每層兩個節點。每個節點的層數就是該節點應當給的編碼長度。

    2. 二叉查找樹
    二叉查找樹(Binary Search Tree)也叫二叉排序樹(Sorted Binary Tree),主要性質是對於二叉樹的每一個節點,如果它有左子樹,那麼左子樹的每個節點的值均小於該節點。如果它有右子樹,那麼右子樹的每個節點的值大於該節點。不允許存在兩個節點的值相等。這樣的樹的結構非常常用,主要目的是二分查找方便。一顆好的二叉查找樹的一次查找複雜度爲O(lgn)

    3. 平衡二叉樹
    平衡二叉樹是對二叉查找樹的優化。二叉查找樹的問題在於,假如樹的左邊和右邊高度差很多,比較傾斜,那麼導致樹的高度被加大了,那麼查找的複雜度事實上是會大於lgn的,最壞可以達到n。因此平衡二叉樹就要儘量保持平衡,他要求每個節點的左子樹和右子樹的高度差不得超過1,一旦發現某個節點出現了失衡的情況,就需要進行旋轉恢復平衡。
    旋轉分爲左左、右右、左右、右左四種情況。左左順時針轉,右右逆時針轉,左右先把下邊逆時針,再把上邊順時針,右左先把下邊順時針,再把上邊逆時針。關鍵要定位到哪個節點出現了失衡的情況,旋轉時需要包括它的父節點。旋轉之後這個失衡點取代了原先父節點的位置,而它的父節點變成了它的子節點。

    4. B樹
    首先糾正一個常見的誤認問題:很多人以爲B樹、B-樹、B+樹是三種樹,其實這個完全是翻譯造成的誤解。B-Tree就是B樹,但很多人中文著作翻譯爲B-樹,再加上還正好有一個B樹的改進版本B+樹,更讓人誤解了,其實B樹和B-樹是一個東西B-Tree。
    B樹(Balanced-Tree)主要解決的是減少IO的問題。由於磁盤的速度很慢,而內存往往不夠用,當樹很大的時候,我們需要把樹的很大一部分留在磁盤上需要時讀取。平衡二叉樹的查詢複雜度已經是lgn,如果想要更低的話,一種想法就是把樹變成多路的,這樣就減少了IO的次數。B樹的思想和它的改良版本被廣泛在數據庫系統中運用。
    B樹相對平衡二叉樹的改良在於,給每個中間節點多於1個的key,從而實現多路。
    B樹是多路的,一顆B樹有一個度m。度爲m的B樹的每一個節點的子樹最多有m顆。中間節點至少要有m/2個子樹(如果m是奇數那麼向上取整)。每個節點存在多個值,這些值用於查詢時的比較,如果這個節點有x個值,那麼它有x+1個子樹(x個值線性排列,那麼包括左右兩端一共有x+1個空擋)。B樹要求所有葉子節點在同一層,是嚴格平衡的。經過這樣的改良,B樹得到了比平衡二叉樹更好的效率。

typedef struct node
{
 int keyNum;
 struct node* parent;
 struct node* child; // 指向子節點線性表,keyNum+1個
 type* key; // 指向key線性表,keyNum個
} node;



    5. B+樹
    B+樹在數據庫和文件系統中被廣泛使用。B+樹是對B樹的改良,基本相同,所以講的都是不同點。B樹中的中間節點存放的值既是值,又是比較用的key。而B+樹的中間節點的key就只是key而已。也就是說,B樹的查詢可能到某個中間節點就完成了,而B+樹必須查到葉子才行,因爲只有葉子的值纔有實際意義,中間節點的key只是用於加快查詢的而已。
    B+樹的每個節點如果有x個key的話,那麼它有x個子樹。第i個子樹的全部key落在(x[i-1], x[i]]前開後閉這個區間內(update:這一點在不同的資料上存在着不一樣的描述,維基百科英文版上描述的是x-1個key對於x個子樹,一些國內的博客則是兩種說法都有,維基百科中文版並沒有說明這個問題。如果讀者有確定答案請在下面評論告訴我,謝謝)。B+樹的葉子上保存了完整的數據信息,同時還有一個指針順序指向下一個葉子節點。
    B+樹由於中間節點僅存儲了key而沒有實際的數據,因此整個樹較小,可以把更多的樹的部分放到內存中,進一步地減少了IO的量。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章