1. 程式人生 > >《深入淺出話資料結構》系列之什麼是B樹、B+樹?為什麼二叉查詢樹不行?

《深入淺出話資料結構》系列之什麼是B樹、B+樹?為什麼二叉查詢樹不行?

本文將為大家介紹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年了,聽說你還不會歸併排序?手把手教你手寫歸併排序演算法
為什麼會有多執行緒?什麼是執行緒安全?如何保證執行緒安全?

覺得文章有用的話,點贊+關注唄,好讓更多的人看到這篇文章,也激勵博主寫出更多的好文章。
更多關於演算法、資料結構和計算機基礎知識的內容,歡迎掃碼關注我的原創公眾號「超悅程式設計」。