今天學習B樹和B+樹,B樹和B+樹都是基於二叉樹的衍生,對於二叉樹不太了解的讀者可以翻看《數據結構:二叉樹》
本文目錄:
B樹
定義及特性
B樹,在寫法上通常是B-樹,這不是減號的意思,只是一種表達方式,它是一種能夠存儲數據、對數據進行排序並允許以O(log n)的時間復雜度運行進行查找、順序讀取、插入和刪除的數據結構。,概括來說是一個節點可以擁有多於2個節點的二叉查找樹。
一個m階的B樹具有如下特點:
-
B樹根節點至少有兩個節點,每個節點可以有多個子樹;
-
每個中間節點都包含k-1個元素和k個子樹,其中 m/2 <= k <= m ;
-
所有的葉子結點都位於同一層;
-
每個節點中的元素從小到大排列,節點當中k-1個元素正好是k個孩子包含的元素的值域分划。
看概念還是挺晦澀的,直接放張圖看看正宗的B樹
可以看出,B樹的節點可以有多個數據,並且可以擁有不只兩個子樹,左邊子樹的數據都比節點的數據小,右邊子樹的數據都比節點的數據大。
比起正常的平衡二叉樹,B樹每個節點顯然能存儲的數據更多,在查找數據方面也顯得比較高效,所以B樹被廣泛應用於磁盤IO讀取較為頻繁的系統中。
查找順序
在B樹中,一般的查找順序如下:
-
從根節點開始,如果查找的數據比根節點小,就去左子樹找,否則去右子樹
-
和子樹的多個關鍵字進行比較,找到它所處的范圍,然后去范圍對應的子樹中繼續查找
-
以此循環,直到找到或者到葉子節點還沒找到為止
保持平衡
B樹查找的高效性是基於其獨特的結構,一旦有數據插入或者刪除,那么B樹就需要調整自身來保持平衡。B樹的平衡特性有三點:
-
葉子節點都在同一層
-
每個節點的關鍵字數 (也就是數據個數) 為子樹個數減一(子樹個數 k 介於 m/2 <= k <= m
-
子樹的數據保證左小右大的順序
舉例子說,一棵4階的B樹,節點最多有4個子樹,每個節點的關鍵字數最少為1,最多為3,插入數據時,如果要插入的子樹的關鍵字數已經是最多,就需要拆分節點,調整B樹的結構。
下面是一張從網上找到的動態圖,完整展示了4階B樹的插入並調整結構的過程。
插入的數據依次是:6 10 4 14 5 11 15 3 2 12 1 7 8 8 6 3 6 21 5 15 15 6 32 23 45 65 7 8 6 5 4,效果圖如下:
我比較懶,所以具體的插入過程就不做一一分析了,圖片插入過程也比較清晰,讀者自己可以慢慢研究。
B+樹
說完B樹,再來說說B+樹,B+樹和結構很類似,但查詢性能上更高,具有如下的特性:
-
有k個子樹的中間節點包含有k個元素(B樹中是k-1個元素),每個元素不保存數據,只用來索引,所有數據都保存在葉子節點;
-
葉子節點中包含了全部元素的信息,按照關鍵字的大小從左到右排序;
-
中間節點的元素同時存在於子節點中,在子節點元素中是最大。
下面放張示例圖:
從圖中可以看出,B+樹中間節點和葉子節點有重復的數據,這里聲明一下,中間節點保存的只是子樹數據的子針,並不是真實的數據,所以中間節點的存儲占用空間較少。
同時,葉子節點之間用指針連在一起,換句話說,葉子節點形成了一個鏈表,把所有的數據都存儲了進來。
為什么這樣設計,比起B樹有什么好處呢?
首先,因為B+樹的中間節點只是保存子樹的最大數據和子樹的子針,本身的占用空間較小,因此可以容納更多節點元素,也就是說同樣數據情況下,B+ 樹會 B 樹更加“矮胖”,因此查詢效率更快。
其次,查找某個范圍的數據,只需在B+樹的葉子節點鏈表中遍歷即可,不需要像B 樹那樣挨個中序遍歷比較大小。總結來說,B+樹的優點就是:
-
層級更低,IO 次數更少;
-
每次都需要查詢到葉子節點;
-
查詢性能穩定葉子節點形成有序鏈表,范圍查詢方便
B+樹的插入
B+樹的插入過程也是比較麻煩的,因為也需要保持平衡,這里也是給大家展示一張動態圖,具體就不分析了。
使用場景
最后說一下B樹和B+樹的使用場景,通過前面的學習,我們了解了這兩種樹都是可以有效減少IO次數的數據結構,基於這個優點,它們被廣泛應用於磁盤文件系統中,
例如 windows的HPFS 文件系統、Linux的文件系統、Mysql的索引等,尤其是Mysql的索引結構,這是面試中的常見問題,所以,了解B樹和B+樹還是非常有必要的。