常见索引结构—B-树
原文作者:解學武
原文地址:B-樹及其基本操作(插入和刪除)詳解
推薦閱讀:漫畫:什么是B-樹??
什么是B-樹?
B-樹,有時又寫為B_樹(其中的“-”或者“_”只是連字符,并不讀作“B減樹”),一顆 m 階的 B-樹,或者本身是空樹,否則必須滿足以下特性:
- 樹中每個結點至多有 m 棵子樹;
- 若根結點不是葉子結點,則至少有兩棵子樹;
- 除根之外的所有非終端結點至少有棵子樹;
- 所有的非終端結點中包含下列信息數據:(n,A0,K1,A1,K2,A2,…,Kn,An);
n 表示結點中包含的關鍵字的個數,取值范圍是:?m/2?-1≤ n ≤m-1。Ki?(i 從 1 到 n)為關鍵字,且 Ki??< Ki+1?;Ai?代表指向子樹根結點的指針,且指針 Ai-1?所指的子樹中所有結點的關鍵字都小于 Ki,An?所指子樹中所有的結點的關鍵字都大于 Kn。
圖1 結點結構如圖 1 所示,當前結點中有 4 個關鍵字,之間的關系為:K1<K2<k3<K4。同時對于 A0?指針指向的子樹中的所有關鍵字來說,其值都要比 K1?小;而 A1?指向的子樹中的所有的關鍵字的值,都比 K1?大,但是都要比 K2?小。
- 所有的葉子結點都出現在同一層次,實際上這些結點都不存在,指向這些結點的指針都為 NULL;
例如圖 2 所示就是一棵 4 階的 B-樹,這棵樹的深度為 4 :
 ?
 在使用 B-樹進行查找操作時,例如在如圖 2 所示的 B-樹中查找關鍵字 47 的過程為:
以圖 2 中的 B-樹為例,若查找到深度為 3 的結點還沒結束,則會進入葉子結點,但是由于葉子結點本身不存儲任何信息,全部為 NULL,所以查找失敗。
B-樹中插入關鍵字(構建B-樹)
B-樹也是從空樹開始,通過不斷地插入新的數據元素構建的。但是 B-樹構建的過程同前面章節的二叉排序樹和平衡二叉樹不同,B-樹在插入新的數據元素時并不是每次都向樹中插入新的結點。
 因為對于 m 階的 B-樹來說,在定義中規定所有的非終端結點(終端結點即葉子結點,其關鍵字個數為 0)中包含關鍵字的個數的范圍是[?m/2?-1,m-1],所以在插入新的數據元素時,首先向最底層的某個非終端結點中添加,如果該結點中的關鍵字個數沒有超過 m-1,則直接插入成功,否則還需要繼續對該結點進行處理。
 假設現在圖 3 的基礎上插入 4 個關鍵字 30、26、85 和 7:
 插入關鍵字 30 :從根結點開始,由于 30 < 45,所以要插入到以 b 結點為根結點的子樹中,再由于 24 < 30,插入到以 d 結點為根結點的子樹中,由于 d 結點中的關鍵字個數小于 m-1=2,所以可以將關鍵字 30 直接插入到 d 結點中。結果如下圖所示:
 插入關鍵字 26:從根結點開始,經過逐個比較,最終判定 26 還是插入到 d 結點中,但是由于 d 結點中關鍵字的個數超過了 2,所以需要做如下操作:
- 關鍵字 37 及其左右兩個指針存儲到新的結點中,假設為 d’ 結點;
- 關鍵字 30 存儲到其雙親結點 b 中,同時設置關鍵字 30 右側的指針指向 d’;
經過以上操作后,插入 26 后的B-樹為:
圖 5 插入關鍵字26后的B-樹插入關鍵字 85:從根結點開始,經過逐個比較,最終判定插入到 g 結點中,同樣需要對 g 做分裂操作:
- 關鍵字 85 及其左右兩個指針存儲到新的結點中,假設為 g’ 結點;
- 關鍵字 70 存儲到其雙親結點 e 中,同時設置 70 的右側指針指向 g’ ;
經過以上操作后,插入 85 后的結果圖為:
圖 6 插入 85 的效果圖圖 6 中,由于關鍵字 70 調整到其雙親結點中,使得其 e 結點中的關鍵字個數超過了 2,所以還需進一步調整:
- 將 90 及其左右指針存儲到一個新的結點中,假設為 e’ 結點;
- 關鍵字 70 存儲到其雙親結點 a 中,同時其右側指針指向 e’ ;
最終插入關鍵字 85 后的 B-樹為:
圖 7 插關鍵字85后的B-樹插入關鍵字 7:從根結點開始依次做判斷,最終判定在 c 結點中添加,添加后發現 c 結點需要分裂,分裂規則同上面的方式一樣,結果導致關鍵字 7 存儲到其雙親結點 b 中;后 b 結點分裂,關鍵字 24 存儲到結點 a 中;結點 a 同樣需要做分裂操作,最終 B-樹為:
圖 8 插入關鍵字7后的B-樹通過上邊的例子,可以總結出一下結論:在構建 B-樹的過程中,假設 p 結點中已經有 m-1 個關鍵字,當再插入一個關鍵字之后,此結點分裂為兩個結點,如下圖所示:
圖 9 B-樹構成過程中的“分裂”提示:如圖 9所示,結點分裂為兩個結點的同時,還分裂出來一個關鍵字 K?m/2?,存儲到其雙親結點中。
B-樹中刪除關鍵字
在 B-樹種刪除關鍵字時,首先前提是找到該關鍵字所在結點,在做刪除操作的時候分為兩種情況,一種情況是刪除結點為 B-樹的非終端結點(不處在最后一層);另一種情況是刪除結點為 B-樹最后一層的非終端結點。
例如圖 3 來說,關鍵字 24、45、53、90屬于不處在最后一層的非終端結點,關鍵字 3、12、37等同屬于最后一層的非終端結點。
如果該結點為非終端結點且不處在最后一層,假設用 Ki?表示,則只需要找到指針 Ai?所指子樹中最小的一個關鍵字代替 Ki,同時將該最小的關鍵字刪除即可。
例如圖 3 中,如果要刪除關鍵字 45 ,只需要使用關鍵字 50 代替 45 ,同時刪除 f 結點中的 50 即可。
如果該結點為最后一層的非終端結點,有下列 3 種可能:
- 被刪關鍵字所在結點中的關鍵字數目不小于?m/2?,則只需從該結點刪除該關鍵字 Ki?以及相應的指針 Ai?。
例如,在圖 3 中,刪除關鍵字 12 ,只需要刪除該關鍵字 12以及右側指向 NULL 指針即可。
- 被刪關鍵字所在結點中的關鍵字數目等于?m/2?-1,而與該結點相鄰的右兄弟結點(或者左兄弟)結點中的關鍵字數目大于?m/2?-1,只需將該兄弟結點中的最小(或者最大)的關鍵字上移到雙親結點中,然后將雙親結點中小于(或者大于)且緊靠該上移關鍵字的關鍵字移動到被刪關鍵字所在的結點中。
例如在圖 3 中刪除關鍵字 50,其右兄弟結點 g 中的關鍵字大于2,所以需要將結點 g 中最小的關鍵字 61 上移到其雙親結點 e 中(由此 e 中結點有:53,61,90),然后將小于 61 且緊靠 61 的關鍵字 53 下移到結點 f 中,最終刪除后的 B-樹如圖 10 所示。
圖 10 刪除結點50后的B-樹- 被刪除關鍵字所在的結點如果和其相鄰的兄弟結點中的關鍵字數目都正好等于?m/2?-1,假設其有右兄弟結點,且其右兄弟結點是由雙親結點中的指針 Ai?所指,則需要在刪除該關鍵字的同時,將剩余的關鍵字和指針連同雙親結點中的 Ki?一起合并到右兄弟結點中。
例如,在圖 10 中 B-樹中刪除關鍵字 53,由于其有右兄弟,且右兄弟結點中只有 1 個關鍵字。在刪除關鍵字 53 后,結點 f 中只剩指向葉子結點的空指針,連同雙親結點中的 61(因為 61 右側指針指向的兄弟結點 g)一同合并到結點 g 中,最終刪除 53 后的 B-樹為:
圖 11 刪除結點53后的B-樹在合并的同時,由于從雙親結點中刪除一個關鍵字,若導致雙親結點中關鍵字數目小于?m/2?-1,則繼續按照該規律進行合并。例如在圖 11 中 B-樹的情況下刪除關鍵字 12 時,結點 c 中只有一個關鍵字,然后做刪除關鍵字 37 的操作。此時在刪除關鍵字 37 的同時,結點 d 中的剩余信息(空指針)同雙親結點中的關鍵字 24 一同合并到結點 c 中,效果圖為:
圖 12 刪除結點 37后的效果圖由于結點 b 中一個關鍵字也沒有,所以破壞了B-樹的結構,繼續整合。在刪除結點 b 的同時,由于 b 中僅剩指向結點 c 的指針,所以連同其雙親結點中的 45 一同合并到其兄弟結點 e 中,最終的B-樹為:
圖 13 刪除37后的B-樹總結
由于 B-樹具有分支多層數少的特點,使得它更多的是應用在數據庫系統中。除了 B-樹,還有專門為文件系統而生的 B+樹,在本章的下一節會詳細介紹。
總結
以上是生活随笔為你收集整理的常见索引结构—B-树的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 常见索引结构—跳跃表
- 下一篇: 常见索引结构—B+树
