B樹的定義
假設B樹的度為t(t>=2),則B樹滿足如下要求:(參考算法導論)
(1) 每個非根節點至少包含t-1個關鍵字,t個指向子節點的指針;至多包含2t-1個關鍵字,2t個指向子女的指針(葉子節點的子女為空)。
(2) 節點的所有key按非降序存放,假設節點的關鍵字分別為K[1], K[2] … K[n], 指向子女的指針分別為P[1], P[2]…P[n+1],其中n為節點關鍵字的個數。則有:
P[1] <= K[1] <= P[2] <= K[2] …..<= K[n] <= P[n+1] // 這里P[n]也指其指向的關鍵字
(3) 若根節點非空,則根節點至少包含兩個子女;
(4) 所有的葉子節點都在同一層。
B樹的搜索,search(root, target)
從root出發,對每個節點,找到大於或等於target關鍵字中最小的K[i],如果K[i]與target相等,則查找成功;否則在P[i]中遞歸搜索target,直到到達葉子節點,如仍未找到則說明關鍵字不在B樹中,查找失敗。
B樹的插入,insert(root, target)
B樹的插入需要沿着搜索的路徑從root一直到葉節點,根據B樹的規則,每個節點的關鍵字個數在[t-1, 2t-1]之間,故當target要加入到某個葉子時,如果該葉子節點已經有2t-1個關鍵字,則再加入target就違反了B樹的定義,這時就需要對該葉子節點進行分裂,將葉子以中間節點為界,分成兩個包含t-1個關鍵字的子節點,同時把中間節點提升到該葉子的父節點中,如果這樣使得父節點的關鍵字個數超過2t-1,則要繼續向上分裂,直到根節點,根節點的分裂會使得樹加高一層。
上面的過程需要回溯,那么能否從根下降到葉節點后不回溯就能完成節點的插入呢?答案是肯定的,核心思想就是未雨綢繆,在下降的過程中,一旦遇到已滿的節點(關鍵字個數為2t-1),就就對該節點進行分裂,這樣就保證在葉子節點需要分裂時,其父節點一定是非滿的,從而不需要再向上回溯。
B樹的刪除,delete(root, target)
在刪除B樹節點時,為了避免回溯,當遇到需要合並的節點時就立即執行合並,B樹的刪除算法如下:從root向葉子節點按照search規律遍歷:
(1) 如果target在葉節點x中,則直接從x中刪除target,情況(2)和(3)會保證當再葉子節點找到target時,肯定能借節點或合並成功而不會引起父節點的關鍵字個數少於t-1。
(2) 如果target在分支節點x中:
(a) 如果x的左分支節點y至少包含t個關鍵字,則找出y的最右的關鍵字prev,並替換target,並在y中遞歸刪除prev。
(b) 如果x的右分支節點z至少包含t個關鍵字,則找出z的最左的關鍵字next,並替換target,並在z中遞歸刪除next。
(c) 否則,如果y和z都只有t-1個關鍵字,則將targe與z合並到y中,使得y有2t-1個關鍵字,再從y中遞歸刪除target。
(3) 如果關鍵字不在分支節點x中,則必然在x的某個分支節點p[i]中,如果p[i]節點只有t-1個關鍵字。
(a) 如果p[i-1]擁有至少t個關鍵字,則將x的某個關鍵字降至p[i]中,將p[i-1]的最大節點上升至x中。
(b) 如果p[i+1]擁有至少t個關鍵字,則將x個某個關鍵字降至p[i]中,將p[i+1]的最小關鍵字上升至x個。
(c) 如果p[i-1]與p[i+1]都擁有t-1個關鍵字,則將p[i]與其中一個兄弟合並,將x的一個關鍵字降至合並的節點中,成為中間關鍵字。
B+樹
與B樹不同的時,B+樹的關鍵字都存儲在葉子節點,分支節點均為索引,在實現上大致與B樹類似,在幾個細節稍有不同。
(1) 數據結構中增加prev,next指針,用於將葉子節點串成有序雙向鏈表。
(2) 在節點分裂的時候,如果分裂的節點為葉子,則需要把中間節點保留在左(或右)邊的分支上,並且需要更新prev和next。
(3) 在節點合的時候,如果合並的節點為葉子,不需要把跟節點下降為中間節點,並且需要更新prev和next。
(4) 在向鄰接節點借節點時,借來的關鍵字並不是父節點的關鍵字,而是鄰接點的關鍵字,並根據實際情況更新父節點的索引。
源碼地址(並且加上了“策略模式”來調用B、B+的方法):我的github上的BTree-and-BPlusTree-Realize項目
B-樹、B+樹、B*樹定義介紹:http://www.cnblogs.com/orange1438/p/4762606.html
作者:orange1438 出處:http://www.cnblogs.com/orange1438/ 本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。