B樹
B樹(B-tree)是一種自平衡的樹,能夠保持資料有序。是一種專用的 M 階樹,最多有 M 個節點的自平衡樹,與自平衡二叉查詢樹不同,B樹 為系統大塊資料的讀寫操作做了優化。B樹 減少定位記錄時所經歷的中間過程,從而加快存取速度。這種資料結構常被應用在 資料庫 和 檔案系統 的實現上。
這種資料結構能夠讓查詢資料、順序訪問、插入資料及刪除的動作,都在對數時間內完成。
一棵 M階 的 B樹 要滿足以下條件
1.每一個節點最多有 M 個子節點。
2.除了根節點和葉子節點外,其他每個節點最少有 M/2 個子節點。
3.根節點不是葉子節點,它至少有 2 個子節點。
4.有 k 個子節點的非葉子節點擁有 k−1 個鍵。
5.所有的葉子節點必須在同一層。
B樹的實現
1.搜尋
B樹的搜尋和二叉搜尋樹類似。從根節點開始,從上到下遞迴的遍歷樹。在每一層上,搜尋的範圍被減小到包含了搜尋值的子樹中。子樹值的範圍被它的父節點的鍵確定。
搜尋下圖中的數字 49
a.用 49 和根節點元素比較,由於 49 < 78,所以繼續遍歷左子樹。
b.由於 40<49<56,所以遍歷節點 40 的右子樹。
c.49>45,往右移。
d.找到 49,返回。
2.插入
從根節點開始,搜尋這棵樹找到新元素應該被新增到的葉子節點。如果節點擁有的元素數量小於最大值,那麼有空間容納新的元素。將新元素插入到這一節點,且保持節點中元素有序。否則的話,需要將它平均地分裂成兩個節點,從葉子節點的元素和新的元素中選擇出中位數,小於這一中位數的元素放入左邊節點,大於這一中位數的元素放入右邊節點,中位數作為分隔值。
分隔值被插入到父節點中,這可能會造成父節點分裂,分裂父節點時可能又會使它的父節點分裂,以此類推。如果沒有父節點(這一節點是根節點),就建立一個新的根節點(增加了樹的高度)。
插入元素 8 到下圖的 5階 B樹中
由於 8 大於 5,插入到 5 的後面。
由於一棵 B樹最多包含 k-1 個鍵,然而這個節點包含了 5 個鍵,所以需要按中位拆分,8 變成父節點。
3.刪除
假如刪除的是葉子節點,將它刪除後,重新進行平衡。
假如刪除的是中間節點,每一個元素都作為分隔兩顆子樹的分隔值,因此我們需要重新劃分。選擇一個新的分隔符(左子樹中最大的元素或右子樹中最小的元素),將它從葉子節點中移除,替換掉被刪除的元素作為新的分隔值。如果刪除的這個葉子節點擁有的元素數量小於最低要求,那麼從這一葉子節點開始重新進行平衡。
刪除下面 5階B樹 為 53 的節點
找到 53 在 49 的右孩子裡,然後刪掉它。
由於 B樹 的中間節點個數不得低於 m/2,現在只剩下一個節點,已經違反了規則,就需要按下面的重新平衡演算法去滿足它的特性,最後這棵樹變成下面這樣的。
重新平衡演算法
如果缺少元素節點的右兄弟節點存在且擁有多餘的元素,將父節點的分隔值複製到缺少元素節點的最後,將父節點的分隔值替換為右兄弟的第一個元素。
如果缺少元素節點的左兄弟節點存在且擁有多餘的元素,將父節點的分隔值複製到缺少元素節點的最後,將父節點的分隔值替換為左兄弟的第一個元素。
如果它的兩個直接兄弟節點都只有最小數量的元素,那麼將它與一個直接兄弟節點以及父節點中它們的分隔值合併,將分隔值複製到左邊的節點,將右邊節點中所有的元素移動到左邊節點,將父節點中的分隔值和空的右子樹移除,如果父節點是根節點並且沒有元素了,那麼釋放它並且讓合併之後的節點成為新的根節點,否則,如果父節點的元素數量小於最小值,重新平衡父節點。
總結
B樹 實際上是二叉搜尋樹的升級版,由於二叉樹只有 2 個子節點,當資料量大的時候樹會變得非常高,也會對效能有一定影響。硬碟的讀取是按塊來讀取的,一般是 4k。因此我們可以根據這一特性,把 2 個節點變為 M 個節點,每次讀取減少定位記錄時所經歷的中間過程,從而加快存取速度。
B樹 在插入和刪除時,都有可能導致破壞 B樹 的規則,因此當破壞規則時,需要對樹進行重新平衡,已到達查詢資料、順序訪問、插入資料及刪除時的時間複雜度在 O(log n)。
B樹 的變體 B+樹,鍵值和記錄儲存在葉子節點,一個葉子節點可以包含一個指標,指向另一個葉子節點以加速順序存取。
B樹 的變體 B*樹,分支出更多的內部鄰居節點以保持內部節點更密集地填充。此變體要求非根節點至少 2/3 填充,而不是 1/2。為了維持這樣的結構,當一個節點填滿之後將不會再立即分割節點,而是將它的鍵值與下一個節點共享。當兩個節點都填滿之後,分割成 3 個節點。
PS:
清山綠水始於塵,博學多識貴於勤。
我有酒,你有故事嗎?
公眾號:「清塵閒聊」。
歡迎一起談天說地,聊程式碼。