第五部分 高級數據結構
第18章 B樹
- B樹是爲磁盤或其他直播存取的輔助存儲設備而設計的一種平衡搜索樹,它們在降低磁盤I/O操作數方面要更好一些。
- B樹的結點可以有很多孩子
1. B樹的定義
一棵B樹T是具有以下性質的有根樹(根爲T.root)
1. 每個結點x有下面屬性:
a. x.n,當前存儲在結點x中的關鍵字個數。
b. x.n個關鍵字本身x.key1, x.key2, …, x.keyx.n,以非降序存放,使用得x.key1 <= x.key2 <= … <= x.keyx.n
c. x.leaf,一個布爾值,如果x是葉結點,則爲TRUE;如果x爲內部結點,則爲FALSE
2. 每個內部結點x還包含x.n + 1個指向其孩子的指針x.c1, x.c2, … , x.cx.n+1。葉結點沒有孩子,所以它們的ci屬性沒有定義
3. 關鍵字x.keyi對存儲在各子樹中的關鍵字範圍加以分割:如果ki爲任意一個存儲在以x.ci爲根的子樹中的關鍵字,那麼
k1 <= x.key1 <= k2 <= x.key2 <= … <= x.keyx.n <= kx.n+1
4. 每個葉結點具有相同的高度,即樹的高度h
5. 每個結點所包含的關鍵字個數有上界和下界。用一個被稱爲B樹的最小度數的固定整數t >= 2來表示這些界:
a. 除了根結點以外的每個結點必須至少有t - 1個關鍵字。因此,除了根結點以外的每個內部結點至少有t個孩子。如果樹非空,根結點至少有一個關鍵字
b. 每個結點至多可包含2t -1個關鍵字。因此,一個內部結點至多可有2t個孩子。當一個結點恰好有2t - 1個關鍵字時,稱該結點是滿的。t = 2時的B樹是最簡單的。每個內部結點有2個,3個或4個孩子,即一棵2-3-4村,然而在實際中,t的值越大,B樹的高度就越小。
2. B樹上的基本操作
搜索B樹
B-Tree-Search(x, k)
i = 1
while i <= x.n and k > x.key(i)
i = i + 1
if i <= x.n and key == x.key(i)
return (x,i)
else if x.leaf
return NIL
else
Disk-Read(x, ci)
return B-Tree-Search(x.ci, k)
創建一棵空的B樹
B-Tree-Create(T)
x = allocate-noe()
x.leaf = true
x.n = 0
Disk-Write(x)
T.root = x
向B樹中插入一個關鍵字
分裂B樹中的結點
B-Tree-Split-Child(x, i)
z = allocate-node()
y = x.ci
z.leaf = y.leaf
z.n = t - 1
for j = 1 to t - 1
z.key(j) = y.key(j+t)
if not y.leaf
for j = 1 to t
z.cj = y.c(j+t)
y.n = t - 1
for j = x.n + 1 downto i + 1
x.c(j+1) = x.cj
x.c(i+1) = z
for j = x.n downto i
x.key(j+1) = x.key(j)
x.key(i) = y.key(t)
x.n = x.n + 1
Disk-Write(y)
Disk-Write(z)
Disk-Write(x)
以沿樹單程下行方式向B樹插入關鍵字
B-Tree-Insert(T, k)
r = T.root
if r.n == 2t - 1
s = allocate-node()
T.root = s
s.leaf = false
s.n = 0
s.ci = r
B-Tree-Split-Child(s, 1)
B-Tree-Insert-Nonfull(s, k)
else
B-Tree-Insert-Nonfull(s, k)
B-Tree-Insert-Nonfull(x, k)
i = x.n
if x.leaf
while i >= 1 and k < x.key(i)
x.key(i+1) = x.key(i)
i = i - 1
x.key(i+1) = k
x.n = x.n + 1
Disk-Write(x)
else
while i >= 1 and k < x.key(i)
i = i - 1
i = i + 1
Disk-Read(x.ci)
if x.ci.n == 2t - 1
B-Tree-Split-Child(x, i)
if k > x.key(i)
i = i + 1
B-Tree-Insert-Nonfull(x.ci, k)
3. 從B樹中刪除關鍵字
從B樹中刪除關鍵字的短程情況:
1. 關鍵字k在結點x中,並且x是葉結點,則從x中刪除k
2. 關鍵字k在結點x中,並且x是內部結點,則做以下操作:
a. 結點x中前於k的子結點y至少包含t個關鍵字,則找出k在以y爲根的子樹中的前驅k’。遞歸地刪除k’,並在x中用k’代替k
b. 如果y有少於t個關鍵字,則檢查結點x中後於k的子結點z。如果z至少有t個關鍵字,則找出k在以z爲根的子樹中的後繼k’。遞歸地刪除k’,並在x中用k’代替k
c. 否則,如果y和z都只含有t - 1個關鍵字,則將k和z的全部合併進y,這樣x就丟失了k和指向z的指針,並且y現在包含2t - 1個關鍵字。然後釋放z並遞歸地從y中刪除k
3. 如果關鍵字k當前不在內部結點x中,則確定必包含k的子樹的根x.ci。如果x.ci只有t-1個關鍵字,必須執行步驟3a或3b來保證降至一個至少包含t個關鍵字的結點。然後,通過對x的某個合適的子結點進行遞歸而結束。
a. 如果x.ci只含有t-1個關鍵字,但是它的一個相鄰的兄弟至少包含t個關鍵字,則將x中的某一個關鍵字降至x.ci中,將x.ci的相鄰左兄弟或右兄弟的一個關鍵字升至x,將該兄弟中相應的孩子指針移到x.ci中,這樣就使得x.ci增加了一個額外的關鍵字。
b. 如果x.ci以及x.ci的所有相鄰兄弟都只包含t-1個關鍵字,則將x.ci與一個兄弟合併,即將x的一個關鍵字移至新合併的結點,使之成爲該結點的中間關鍵字。