1. 程式人生 > >B樹的增刪改查

B樹的增刪改查

B-樹,即為B樹。因為B樹的原英文名稱為B-tree,而國內很多人喜歡把B-tree譯作B-樹,B-tree就是指的B樹

B-樹容易讓人誤解,建議大家用B樹稱呼, 本文以下直稱B樹

對概念不理解的可以參考 我的另一篇介紹B樹概念和應用的部落格: 

https://blog.csdn.net/q5706503/article/details/84729768

B樹的基本概念

B樹中結點允許擁有孩子結點個數的最大值成為B樹的階,通常用m表示,從查詢效率考慮,一般要求m>=3。

一棵m階B樹或者是一棵空樹,或者是滿足以下條件的m叉樹:

  • 每個節點最多有m個孩子(分支)。
  • 而最少分支數要看是否為根結點,如果是根結點且不是葉子結點,則至少要有兩個分支,非根非葉結點至少有ceil(m/2)個分支,這裡ceil代表向上取整。
  • 如果一個結點有n-1個關鍵字,那麼該結點有n個分支。這n-1個關鍵字按照遞增順序排列。
  • 結點內各關鍵字按從小到大排列。
  • 葉子結點處於同一層;可以用空指標表示,是查詢失敗到達的位置。

每個結點的結構為:

n k1 k2 ... kn
p0 p1 p2 ... pn

其中,n為該結點中關鍵字的個數;ki為該結點的關鍵字且滿足ki<ki+1;pi為該結點的孩子結點指標且滿足pi所指結點上的關鍵字大於ki且小於ki+1,p0所指結點上的關鍵字小於k1,pn所指結點上的關鍵字大於kn。

B-樹是平衡m叉查詢樹,但限制更強,要求所有葉結點都在同一層。

舉個例子:

這是一個4階B樹(m=4)

B tree

分析解釋: 

  • 結點的分支數等於關鍵字數+1,最大的分支數就是B樹的階數,因此m階的B樹中結點最多有m個分支,所以可以看到,上面的一棵樹是一個4階B樹
  • 因為上面是一棵4階B樹,所以非根非葉結點至少要有ceil(4/2)=2個分支。根結點可以不滿足這個條件,圖中的根結點有兩個分支
  • 如果根結點中沒有關鍵字就沒有分支,此時B-樹是空樹,如果根結點有關鍵字,則其分支數比大於或等於2,因為分支數等於關鍵字數+1
  • 上圖中除根結點外,結點中的關鍵字個數至少為1,因為分支數至少為2,分支數比關鍵字數多1,還可以看出結點內關鍵字都是有序的,並且在同一層中,左邊結點內所有關鍵字均小於右邊結點內的關鍵字,例如,第二層上的兩個結點,右邊結點內的關鍵字為11,15,他們均大於左邊結點內的關鍵字4
  • B樹一個很重要的特徵是,下層結點內的關鍵字取值總是落在由上層結點關鍵字所劃分的區間內,具體落在哪個區間內可以由指向它的指標看出
  • 上圖中葉子結點都在同一層

 查詢:

B-樹的查詢很簡單,是二叉排序樹的擴充套件,二叉排序樹是二路查詢,B-樹是多路查詢,因為B-樹結點內的關鍵字是有序的,在結點內進行查詢時除了順序查詢外,還可以用折半查詢來提升效率。B-樹的具體查詢步驟如下(假設查詢的關鍵字為key):
1)先讓key與根結點中的關鍵字比較,如果key等於k[i](k[]為結點內的關鍵字陣列),則查詢成功
2)若key<k[1],則到p[0]所指示的子樹中進行繼續查詢(p[]為結點內的指標陣列),這裡要注意B-樹中每個結點的內部結構。
3)若key>k[n],則道p[n]所指示的子樹中繼續查詢。
4)若k[i]<key<k[i+1],則沿著指標p[I]所指示的子樹繼續查詢。
5)如果最後遇到空指標,則證明查詢不成功。

拿上面的二叉樹進行舉例,比如我們想要查詢關鍵字10,下圖加粗的部分顯示了查詢的路徑:

 

插入:

與二叉排序樹一樣,B-樹的建立過程也是將關鍵字逐個插入到樹中的過程。
在進行插入之前,要確定一下每個結點中關鍵字個數的範圍,如果B-樹的階數為m,則結點中關鍵字個數的範圍為ceil(m/2)-1 ~ m-1個。
對於關鍵字的插入,需要找到插入位置。在B-樹的查詢過程中,當遇到空指標時,則證明查詢不成功,同時也找到了插入位置,即根據空指標可以確定在最底層非葉結點中的插入位置,為了方便,我們稱最底層的非葉結點為終端結點,由此可見,B-樹結點的插入總是落在終端結點上。在插入過程中有可能破壞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}

刪除:

刪除步驟:

  • 搜尋要刪除的節點值
  • 若該節點為葉子節點 : 直接刪除該數,  判斷刪除後是否失衡, 若失衡執行下面的失衡調整方法:
  • 若是非葉子節點 :  用相鄰葉子節點 ( 左孩子的最大值/右孩子的最小值, 最後有介紹 ) 替換該節點值,  刪除該替換節點值  問題就轉換為了刪除葉子節點的值
  • 失衡調整方法:
    1. 先向左右兄弟節點借值 , 借前判斷左右兄弟節點的數量是否大於節點允許的最小個數(若節點內個數等於最小允許數則執行下面的方法)
    2.若左右兄弟節點的節點數不足 則 與兄弟節點(左右都可)與涉及到的父節點分割值合併, 合併之後判斷父節點是否失衡(因為父節點中值個數-1可能失衡), 若失衡則對父節點遞迴呼叫該失衡調整方法

上述刪除步驟不包含對根節點刪除或節點很少時根節點的孩子刪除的情況的判斷(較為簡單,自行拓展)

刪除要考慮樹中是否允許出現相同的數, 像上面的例子是可以可以重複的, 能否重複看具體需求, 最好不允許重複,因為有重複資料會使刪除操作變複雜, 當然可以對重複資料刪除很多次直到找不到該資料, 大家可能有更好的辦法解決, 不過終究降低了刪除的效率, 這裡換一個沒有重複數值的例子來示範刪除:

該示例刪除8、16、15、4這4個關鍵字。
(1)刪除關鍵字8、16。關鍵字8在終端結點上,並且刪除後其所在結點中關鍵字的個數不會少於2,因此可以直接刪除。

關鍵字16不在終端結點上,但是可以用17來覆蓋16,然後將原來的17刪除掉,這就是上面提到的用相鄰節點 ( 左孩子的最大值/右孩子的最小值 ) 替換該節點值操作。

這裡不用15和16進行關鍵字交換,因為這樣會導致15所在結點中關鍵字的個數小於2。因此,刪除8和16之後B樹如下圖所示:

(2)刪除關鍵字15,15雖然也在終端結點上 , 刪除後當前結點中關鍵字的個數小於2。這裡需要向其兄弟結點借關鍵字,顯然應該向其右兄弟來借關鍵字,因為左兄弟的關鍵字個數已經是下限2.借關鍵字不能直接將18移到15所在的結點上,因為這樣會使得15所在的結點上出現比17大的關鍵字,所以正確的借法應該是先用17覆蓋15,在用18覆蓋原來的17,最後刪除原來的18,刪除關鍵字15後的B-樹如下圖所示:

(3)刪除關鍵字4,4在終端結點上,但是此時4所在的結點的關鍵字個數已經到下限,需要借關鍵字,不過可以看到其左右兄弟結點已經沒有多餘的關鍵字可借。

所以就需要與兄弟節點(左右都可)與涉及到的父節點分割值合併。先將關鍵字4刪除,然後將關鍵字5、6(涉及到的父節點分割值)、7、9(兄弟節點)進行合併作為一個結點連結在關鍵字3右邊的指標上,也可以將關鍵字1、2、3、5合併作為一個結點連結在關鍵字6左邊的指標上,如下圖所示:

顯然上述兩種情況下都使父節點失衡,即出現了非根的結點孩子數小於ceil(m/2),需要繼續進行合併(對父元素遞迴失衡調整方法),合併後的B樹如下圖所示:

找a的相鄰關鍵字的方法為:沿著a的左指標來到其子樹根結點,然後沿著根結點中最右端的關鍵字的右指標往下走,用同樣的方法一直走到葉結點上,葉結點上的最右端的關鍵字即為a的相鄰關鍵字(這裡找的是a左邊的相鄰關鍵字,我們可以用同樣的思路找到a右邊的相鄰關鍵字)。可以看到下圖中a的相鄰關鍵字是d和e,要刪除關鍵字a,可以用d來取代a,然後按照上面的情況刪除葉子結點上的d即可。

B-樹的高度及效能分析 

     B-樹上操作的時間通常由存取磁碟的時間和CPU計算時間這兩部分構成。B-樹上大部分基本操作所需訪問盤的次數均取決於樹高h。關鍵字總數相同的情況下B-樹的高度越小,磁碟I/O所花的時間越少。
     與高速的CPU計算相比,磁碟I/O要慢得多,所以有時忽略CPU的計算時間,只分析演算法所需的磁碟訪問次數(磁碟訪問次數乘以一次讀寫盤的平均時間(每次讀寫的時間略有差別)就是磁碟I/O的總時間)。

1、B-樹的高度
     定理9.1 若n≥1,m≥3,則對任意一棵具有n個關鍵字的m階B-樹,其樹高h至多為:
        logt((n+1)/2)+1。
這裡t是每個(除根外)內部結點的最小度數,即
         
     由上述定理可知:B-樹的高度為O(logtn)。於是在B-樹上查詢、插入和刪除的讀寫盤的次數為O(logtn),CPU計算時間為O(mlogtn)。

2、效能分析
  ①n個結點的平衡的二叉排序的高度H(即lgn)比B-樹的高度h約大lgt倍。
     【例】若m=1024,則lgt=lg512=9。此時若B-樹高度為4,則平衡的二叉排序樹的高度約為36。顯然,若m越大,則B-樹高度越小。
  ②若要作為記憶體中的查詢表,B-樹卻不一定比平衡的二叉排序樹好,尤其當m較大時更是如此。
     因為查詢等操作的CPU計算時間在B-樹上是
        O(mlogtn)=0(lgn·(m/lgt))
而m/lgt>1,所以m較大時O(mlogtn)比平衡的二叉排序樹上相應操作的時間O(lgn)大得多。因此,僅在記憶體中使用的B-樹必須取較小的m。(通常取最小值m=3,此時B-樹中每個內部結點可以有2或3個孩子,這種3階的B-樹稱為2-3樹)。

 

例子有參考以下地址文章

https://www.jianshu.com/p/7dedb7ebe033

插入的第一張圖片和gif動圖來自以下博主:(這個動圖真的牛逼)

http://www.cnblogs.com/yangecnu/p/Introduce-B-Tree-and-B-Plus-Tree.html