一、什麼是B樹?
B樹是一棵是具備以下特點的有根樹。
1、節點屬性
a)x.n:為節點中儲存的關鍵字個數。
b)x.key:為節點中儲存的關鍵字。x.key1、x.key2 ... x.keyx.n 以非降序順序排列,滿足 x.key1 <= x.key2 ... <= x.keyx.n。
c)x.leaf:為當前節點是否為葉子節點(true | false)
d)x.c:為指向子節點的指標,內部節點包含指標個數為 x.n + 1,葉子節點沒有子節點,所以沒有此屬性。
2、分割
關鍵字 x.key 對儲存在子樹中的關鍵字進行分割。某個子節點的所有關鍵字值範圍總是在節點 x 的某兩個關鍵字之間。這個值可能是任何可排序的表示,比如:
3、深度
每個葉子節點具有相同的深度,即樹的高度(由根節點到葉子節點的路徑長度)。
4、度數
每個節點包含的關鍵字個數有上下界限制。基本表示單位為B樹的最小度數 t(滿足 t >= 2):
a)除了根節點外(空樹沒有關鍵字,非空樹根節點至少包含一個關鍵字),每個節點至少有 t - 1 個關鍵字,進而可以推導,每個內部節點至少有 t 個子節點【1.d】。
b)每個節點至多包含 2t - 1 個關鍵字(此時稱之為【滿】 狀態),進而可以推導,每個內部節點至多有 2t 個子節點【1.d】。
二、B數的高度
首先樹的根節點至少包含 1 個關鍵字,其它節點至少包含 t - 1 個關鍵字,至少有 t 個子節點【一.4.a】。
我們知道 B 樹的度數 t >= 2,所以:
深度為 1 的位置上至少有 2 個節點。
深度為 2 的位置至少有 2 * t 個 節點。
深度為 3 的位置至少有 2 * t * t 個 節點。
... ...
深度為 h 的位置至少有 2 * t * ... * t 個 節點。
圖示:
【1】所以所有非根節點個數至少為:2 + 2 * t + 2 * t * t + 2 * t * ... * t = 2 * t0 + 2 * t1+ 2 * t2 + 2 * th-1,標識為 sum(node)
【2】相應的非根節點關鍵字個數至少為:(t - 1) * sum(node)
【3】那麼總的關鍵字個數至少為: 1 + (t - 1) * sum(node)
【4】我們用 n 表示關鍵字個數,所以存在 n >= 1 + (t - 1) * sum(node),代入【1】中的求和,最終經過一系列的變換,可以得出B樹的高度滿足:h <= logt(n+1)/2。
三、B樹的搜尋
假定我們要查詢的關鍵字為 k,入口節點 x:
a)需要找到 k 在 x 所有關鍵字中的位置,臨界關鍵字 keyi 滿足 k <= keyi 。
b)如果存在 k == keyi 那麼查詢結束,否則繼續。
c)如果 x 為葉子節點,則查詢結束,否則繼續
d)由 keyi 臨界關鍵字,我們可以得到相應指向子節點的指標 ci。
然後,繼續由 ci 指向的子節點作為入口節點,繼續上述過程。
四、B樹的插入
B樹插入新關鍵字後,必須仍然是一顆合法的B樹。
由【一.4.b】我們直到 B 樹節點存在一種狀態【滿】,即當前節點關鍵字個數為 2t -1。【滿】狀態的節點插入新節點必須經過特定的前置處理:分裂。
所謂分裂,即將節點由中間關鍵字作為分割點,分割為兩個節點,每個節點包含 t - 1 個關鍵字,中間節點 x.kt 則上升到父節點中,作為兩棵子樹的劃分點,參見【一.2】。
此處需要注意的是,如果父節點同樣為【滿】節點,那麼在分割點上升之前,同樣需要對父節點執行【分裂】操作。
滿節點的分裂行為會沿著樹向上傳播直到不再需要分裂為止。
上面我們描述的過程,是一個自下而上的【滿】狀態分裂傳播行為。
我們知道,要實現節點的插入,首先需要經過一個B樹的搜尋查詢的過程,搜尋過程自上而下。
顯然,兩個過程,有些重複,我們需要的是單向查詢插入。
鑑於此,在執行查詢的過程中,遇到路徑上的滿節點,則執行分裂操作,直到找到位置插入節點,這樣就避免了自下而上的【分裂】傳播行為。
五、B樹的刪除
B樹刪除特定關鍵字後,必須仍然是一顆合法的B樹。
B樹的插入是一個對節點最大關鍵字數量的約束滿足過程,相應的,B樹的刪除是一個對節點最小關鍵字數量的約束滿足過程。
保障沿途節點關鍵字數量至少為度數 t,一遍自根而下執行刪除。