本文將為大家介紹B樹和B+樹,首先介紹了B樹的應用場景,為什么需要B樹;然后介紹了B樹的查詢和插入過程;最后談了B+樹針對B樹的改進。
在談B樹之前,先說一下B樹所針對的應用場景。那么B樹是用來做什么的呢? B樹是一種為輔助存儲設計的一種數據結構,普遍運用在數據庫和文件系統中。舉個例子來說,數據庫大家肯定都不陌生,比如現在有一張表,其中有100萬條記錄,現在要查找查找其中的某條數據,如何快速地從100萬條記錄中找到需要的那條記錄呢?大家的第一反應肯定是二叉查找樹,下面先談談為什么二叉樹不行。
為什么二叉查找樹不行
還是剛剛那個例子,現在一張表中有100萬條記錄,我們以表的主鍵來構建一個二叉查找樹。先不考慮二叉樹不平衡退化成鏈表的情況,假設是理想情況,這個二叉樹是完全平衡的。那么構建完成之后應該是下面這個樣子的(示意圖)

樹的高度應該為

現在開始查找,加入我們要查找值為100的節點,怎么找呢?首先應該獲取樹的根節點。一般來說,表的索引本身也很大,不可能全部存儲在內存中,因此索引往往以索引文件的形式存儲的磁盤上。也就是說我們構建的二叉查找樹太大了,內存放不下,一般要存在磁盤上,用的時候再從磁盤讀入到內存。現在假設我們知道了根節點所在的磁盤位置,那么應該首先將根節點讀入內存中,這里進行了一次IO操作,然后判斷要找的值比根節點大還是小,100比4大,所以去右子樹查找。那么如何找到6所在的節點呢?根節點中存儲着6所在節點在磁盤上存放的位置。同樣,需要將其先讀入內存中,然后再判斷繼續向下查找,這里又是一次IO操作。下面的過程類似,不再展開。總結一下,查找到我們所需的那條記錄大概需要進行20次IO操作,也就是樹的高度,因為每向下查找一層,就要進行一次IO操作。
為什么要強調IO操作,而不是在內存中比較的次數呢? 因為磁盤的速度相比內存而言是非常的慢。比如常見的7200RPM的硬盤,搖臂轉一圈需要60/7200≈8.33ms,換句話說,讓磁盤完整的旋轉一圈找到所需要的數據需要8.33ms,這比內存常見的100ns慢100000倍左右,這還不包括移動搖臂的時間。所以在這里制約查找速度的不是比較次數,而是IO操作的次數。換句話說,如果能夠減少一次IO操作,那么多在內存中比較100次也無所謂,因為二者的速度相差100000倍。而我們可以認為IO操作的次數就大約等於樹的高度,樹的高度是如何計算的呢?看一下這個公式

我們想讓這個值盡可能的小,就只能讓真數小,或者底數大。真數是數據記錄的條數,不是我們能決定的。那么底數呢?這個2來自哪呢?當然是來自二叉樹中的那個二。那么這個底數能不能變大呢?當然能!!!那就是不要用二叉樹,而要用多叉樹,這就是我們要說的B樹了。
B樹是什么
B樹也稱B-樹,它是一顆多路平衡查找樹。B樹和后面講到的B+樹都是從最簡單的二叉樹變換而來的。描述一顆B樹時需要指定它的階數,階數表示了一個節點最多有多少個孩子節點,一般用字母m表示階數。下面我們來看看B樹的定義
(1)樹中每個節點至多有m 棵子樹(m指的是樹的階);
解釋:有的定義說的是每個節點最多有m-1個關鍵字,是一樣的,對於每個節點來說子樹的數目等於關鍵字數目加1,下面會舉例說明。
(2)若根結點不是葉子結點,則至少有兩棵子樹;
解釋:當根節點為葉子節點時,可以沒有子樹;不為葉子節點時,至少有一個關鍵字,也就是至少有兩棵子樹。
(3)除根結點之外的所有非葉子結點至少有⌈m/2⌉個子節點;
解釋:⌈m/2⌉表示向上取整,比如m=5時,⌈m/2⌉=3,表示至少有3個子節點,當然最多有5個。
(4)所有的非葉子結點中包含以下數據:(n,A0,K1,A1,K2,…,Kn,An)
解釋:n為節點中關鍵字的數目,Ki(i=1,2,…,n)為關鍵字,且Ki<Ki+1
Ai 為指向孩子節點的指針(i=0,1,…,n),且指針Ai-1 所指子樹中所有結點的關鍵字均小於Ki (i=1,2,…,n),An 所指子樹中所有結點的關鍵字均大於Kn。(這里也可以看到對於每個節點來說子節點數量比關鍵字數量多1)
(5)所有的葉子結點都出現在同一層次上,即所有葉節點具有相同的深度,等於樹高度。葉子節點除了包含了關鍵字和關鍵字記錄的指針外也有指向其子節點的指針只不過其指針地址都為null。
下面具體舉個例子說明,相信看了這個例子會對上面的定義有更加深刻的理解。

B樹的查找
以上圖為例,假設要查找15的節點,查找流程如下
(1)獲取根節點的關鍵字進行比較,當前根節點關鍵字為50,50>15,所以找到指向左邊的子節點;
(2)拿到關鍵字10和30,10<15<30 所以直接找到10和30中間的指針指向的子節點;
(3)拿到關鍵字15,就是要查找的目標值, 所以直接返回關鍵字和指針信息(如果樹結構里面沒有包含所要查找的節點則返回null)
至此我們便完成了B樹的查找過程,比較簡單,且與二叉查找樹類似。
關於B樹的插入操作,可以參考【為什么有紅黑樹?什么是紅黑樹?看完這篇你就明白了】這篇推文中關於2-3樹的插入操作的詳細介紹,其實2-3樹就是一種特殊的B樹。限於篇幅,本文不再贅述。
從B樹到B+樹
B+樹是從B樹衍生而來的,比B樹更具有優越性。B+樹相對於B樹主要做了兩點改進:
(1)非葉結點僅具有索引作用,跟記錄有關的信息均存放在葉結點中。
解釋:B+樹的非葉子節點不保存關鍵字記錄的指針,只進行數據索引;B+樹葉子節點保存了父節點的所有關鍵字記錄的指針,所有數據地址必須要到葉子節點才能獲取到。還是舉剛才B樹的例子,B樹中根節點的關鍵字為50。假如我們就要查找主鍵為50的記錄,那么在B樹中只需進行一次IO操作,將根節點讀入內存,便可以直接命中。而在B+樹中則不同,B+樹中任何查詢必須要到葉子節點才能獲取到,所以每次數據查詢的次數都一樣,這一點和B樹有很大區別。
(2)樹的所有葉節點構成一個有序鏈表,可以按照關鍵字排序的次序遍歷全部記錄。
解釋:這樣做有兩點好處。一是進行范圍查詢更快,數據緊密性很高,緩存的命中率也會比B樹高。二是B+樹全節點遍歷更快,B+樹遍歷整棵樹只需要遍歷所有的葉子節點即可,而不需要像B樹一樣需要對每一層進行遍歷,這有利於數據庫做全表掃描。
推薦閱讀
為什么有紅黑樹?什么是紅黑樹?看完這篇你就明白了
都2020年了,聽說你還不會歸並排序?手把手教你手寫歸並排序算法
為什么會有多線程?什么是線程安全?如何保證線程安全?
覺得文章有用的話,點贊+關注唄,好讓更多的人看到這篇文章,也激勵博主寫出更多的好文章。
更多關於算法、數據結構和計算機基礎知識的內容,歡迎掃碼關注我的原創公眾號「超悅編程」。

