1. 程式人生 > >演算法導輪之B樹的學習

演算法導輪之B樹的學習

最近學習了演算法導輪裡B樹相關的知識,在此寫一篇部落格作為總結。

1.引言

B樹是為磁碟或其他直接存取的輔助儲存裝置而設計的一種平衡搜尋樹。B樹類似於紅黑樹,但它與紅黑樹最大不同之處在於B樹的節點可以擁有很多孩子,因此B樹的高度會比紅黑樹小很多,也因此B樹在磁碟I/O方面表現要比紅黑樹好。(對於磁碟操作最耗時的部分在於磁碟讀寫,而每次讀取一個新的樹的節點就必須進行一次磁碟讀取,因此節點較大、樹高度較小的B樹會進行較少的磁碟I/O操作)

2.B樹的定義

一顆B樹的定義如下:
  1. 每個節點x均有如下屬性:
    • n表示儲存在該節點的關鍵字個數
    • n個關鍵字本身key1、key2……keyn以非降序存放,即key1 <= key2 <= …… <= keyn
    • 一個leaf布林值表示該節點是否為葉節點
  2. 每個內部節點包含了n+1個孩子,葉節點沒有孩子
  3. 關鍵字keyi對儲存在各子樹中的關鍵字範圍加以分割:即比keyi小的元素在其左子樹,比keyi大的元素在其右子樹
  4. 每個葉節點具有相同的深度
  5. 每個節點包含的關鍵字個數有上界與下界。我們定義B樹的最小度數為t,則除根節點外的每個節點至少有t-1個關鍵字,每個節點最多有2t-1個關鍵字(即每個節點最少有t個孩子,最多有2t個孩子)(當一個節點有2t-1個關鍵字時,我們稱它為滿的)。
B樹的示意圖如下: B樹
上圖是一個最小度數為2的B樹,因此每個節點擁有1個、2個或3個元素,擁有2個、3個或4個孩子,也被稱為2-3-4樹。 根節點只有一個元素,因此它擁有兩個孩子;兩個孩子分別擁有3個和2個元素,因此他們分別擁有4個和3個孩子。 看到DEF葉節點位於關鍵字C與G的中間,表明了關鍵字對於儲存在各子樹中的關鍵字範圍進行了分割,其餘同理。

3.B樹的插入

要講到樹,就不得不提樹中關鍵的插入與刪除操作,這裡我們先總結B樹的插入操作。 當我們往B樹中插入一個新的關鍵字時,由於B樹節點的關鍵字是受到限制的,因此當一個節點關鍵字數目為2t-1時(該節點是滿的),我們就必須進行分裂操作。

分裂節點

分裂節點的主要操作為把滿節點的中間關鍵字提升至父節點,把原滿節點分裂為中間關鍵字的兩個左右節點 其示意圖如下: B樹分裂
對於某個非滿的節點x,若其孩子節點x.ci為滿的(即孩子節點的關鍵字數目為2t-1)。則把其孩子節點的中間關鍵字(S)提升為父節點(x節點)的關鍵字,並把原孩子節點(x.c節點)分成兩個t-1個關鍵字的節點,分別位於中間關鍵字(S)的左、右。 還有一種比較特殊的情況就是B樹根的分裂: B樹根分裂

分裂是B樹長高的唯一途徑,因此分裂是非常重要的。

插入

講完分裂操作在講插入操作就非常簡單了。插入的時候我們通過比較不斷地根據關鍵字的值尋找孩子節點,當發現一個滿的節點時便分裂,最後找到對應的葉節點時根據關鍵字的值插入相應位置即可。 下面是一個插入關鍵字的例子: B樹插入1
B樹的初始狀態如圖所示,這是一顆最小度數為3的B樹,即他的關鍵字個數為2~5個。 B樹插入2
插入關鍵字B,在根節點由於(B < G)往進入G的左節點,到達葉節點後新增至A與C關鍵字之間。 B樹插入3
插入關鍵字Q:
  1. 在根節點,由於P < Q 而且 Q < X,進入P與X之間的子節點
  2. 發現該子節點是滿的,則進行分裂,把關鍵字T上升到父節點,原子節點分為RS與UV,分為在T關鍵字的左右
  3. 由於Q < T,於是進入T的左子節點
  4. 在RS葉節點中找到對應位置並加入
B樹插入4
插入關鍵字L:
  1. 發現根節點是滿的,分裂根節點,上移P
  2. L < P 進入P的左子節點
  3. G < L < M,進入G與M關鍵字間的節點
  4. 在葉節點的相應位置中插入
B樹插入5
插入關鍵字F:
  1. F < P,進入P的左子節點
  2. F < G,進入G的左子節點
  3. 發現滿的節點,分裂,上移C
  4. F > C,進入C的右子節點
  5. 在葉節點的相應位置中插入

4.B樹的刪除

講完了B樹的插入操作,我們再來講講B樹的刪除操作。 對於刪除操作,我們必須保證每個節點在刪除前必須至少有t(最小度數)個關鍵字。 首先我們把要刪除的關鍵字(假設為k)分兩種情況:
  • 關鍵字k在葉節點中:直接刪除
  • 關鍵字k在內部節點中,分三種情況:
    • k的左子節點擁有t個關鍵字,則把k的左子節點的最後一個關鍵字(假設為j)上移到父節點,然後遞迴的刪除j
    • k的右子節點擁有t個關鍵字,則把k的右子節點的第一個關鍵字(假設為l)上移到父節點,然後遞迴的刪除l
    • k的左右子節點都只有t-1個關鍵字,則把k下降與左右子節點合併成一個擁有2t-1個關鍵字的節點,然後遞迴的刪除k
然後我們再定義一些在尋找刪除節點路上的操作:如果在尋找刪除節點的路上,我們發現某個節點關鍵字數只有t-1個關鍵字,則分兩種情況:
  1. 看該節點的相鄰兄弟節點是否含有至少t個關鍵字,如果是則向相鄰的兄弟節點“借一個關鍵字”(一該節點的左節點為例:把左節點的最後一個關鍵字上升至父節點,然後父節點位置的節點下移到關鍵字個數為t-1的節點上)
  2. 如果該節點相鄰的兄弟節點都只含有t-1個關鍵字,則選擇一個兄弟節點合併,並把兩兄弟之間的父節點下移
下面我們來看一個B樹刪除的例子: B樹刪除1
B樹的初始狀態如圖,這是一顆最小度數為3的B樹,即每個節點擁有2~5個關鍵字。 B樹刪除2
刪除F操作:
  1. F < P,進入P的左子節點
  2. C < F < G,進入C與G之間的子節點
  3. 在葉節點中刪除F
B樹刪除3
刪除M操作:
  1. M < P,進入P的左子節點
  2. 在內部節點中發現M,檢視M的左子節點JKL,擁有3個關鍵字,則把最後一個關鍵字L上升至M的位置,遞迴的刪除L
  3. 節點JKL是葉節點,直接刪除L即可
B樹刪除4
刪除G操作:
  1. G < P,進入P的左子節點
  2. 在內部節點中發現G,檢視G的左右子節點均只有2個關鍵字(不足最小度數3個),下降G關鍵字併合並其左右子節點
  3. 節點DEGJK為葉節點,直接刪除G即可
B樹刪除5
刪除D操作:
  1. D < P,進入P的左子節點
  2. 發現內部節點CL只有2個關鍵字(不足最小度數3個),其兄弟節點也只有2個關鍵字,下降父節點P,與兄弟節點一起合併成一個節點
  3. 由於C < D < L,進入C與L間的子節點
  4. DEJK節點是葉節點,直接刪除D即可
B樹刪除6
刪除B操作:
  1. B < C,進入E的左子節點
  2. 發現節點AC只有兩個關鍵字,其兄弟節點EJK有三個關鍵字,則E上移到父節點,C下移到子節點,變為節點ABC
  3. 節點ABC為葉節點,直接刪除B即可