1. 程式人生 > >資料結構--B 樹、B+ 樹、B* 樹

資料結構--B 樹、B+ 樹、B* 樹

1.  B 樹、B+ 樹、B* 樹

1.1. 前言

前面討論的二叉查詢樹(Binary Search Tree),平衡二叉查詢樹(Balanced BinarySearch Tree),紅黑樹(Red-BlackTree )都是內查詢演算法,被查詢的資料都在記憶體。當查詢的資料放在外存,用平衡二叉樹作磁碟檔案的索引組織時,若以結點為內外存交換的單位,則找到需要的關鍵字之前,平均要進行lgn次磁碟讀操作,而磁碟、光碟的讀寫時間要比隨機存取的記憶體代價大得多。其二,外存的存取是以“頁”為單位的,一頁的大小通常是4K位元組或2048位元組。

因此咱們有面對這樣一個實際問題:就是大規模資料儲存中,實現索引查詢這樣一個實際背景下,樹節點儲存的元素數量是有限的(如果元素數量非常多的話,查詢就退化成節點內部的線性查找了),這樣導致二叉查詢樹結構由於樹的深度過大而造成磁碟I/O讀寫過於頻繁,進而導致查詢效率低下

(為什麼會出現這種情況,待會在背景知識介紹中有所解釋),那麼如何減少樹的深度(當然是不能減少查詢的資料量),一個基本的想法就是:採用多叉樹結構(由於樹節點元素數量是有限的,自然該節點的子樹數量也就是有限的)。

也就是說,因為磁碟的操作費時費資源,如果過於頻繁的多次查詢勢必效率低下。那麼如何提高效率,即如何避免磁碟過於頻繁的多次查詢呢?根據磁碟查詢存取的次數往往由樹的高度所決定,所以,只要我們通過某種較好的樹結構減少樹的結構儘量減少樹的高度,那麼是不是便能有效減少磁碟查詢存取的次數呢?那這種有效的樹結構是一種怎樣的樹呢?

針對上述特點,1972年R.Bayer和E.M.Cright提出了一種B-樹的多路平衡查詢樹,以適合磁碟等直接存取裝置上組織動態查詢表(wikipedia

中:http://en.wikipedia.org/wiki/B-tree,闡述了B-tree名字來源以及相關的開源地址)。B-樹上演算法的執行時間主要由讀、寫磁碟的次數來決定,故一次I/O操作應讀寫儘可能多的資訊。因此B-樹的結點規模一般以一個磁碟頁為單位。一個結點包含的關鍵字及其孩子個數取決於磁碟頁的大小。

這篇文章所要闡述的第一個主題B-tree,即B樹結構(後面,我們將看到,B樹的各種操作能使B樹保持較低的高度,從而達到有效避免磁碟過於頻繁的查詢存取操作,從而有效提高查詢效率)。

在開始介紹B-tree之前,先了解下相關的硬體知識,才能很好的瞭解為什麼需要B-tree這種外存資料結構。 

1.2. 背景知識介紹

B樹和B+廣泛應用於檔案儲存系統以及資料庫系統中,在講解應用之前,我們看一下常見的儲存結構:


我們計算機的主存基本都是隨機訪問儲存器(Random-Access Memory,RAM),他分為兩類:靜態隨機訪問儲存器(SRAM)和動態隨機訪問儲存器(DRAM)。SRAM比DRAM快,但是也貴的多,一般作為CPU的快取記憶體,DRAM通常作為記憶體。這類儲存器他們的結構和儲存原理比較複雜,基本是使用電訊號來儲存資訊的,不存在機器操作,所以訪問速度非常快,具體的訪問原理可以檢視CSAPP,另外,他們是易失的,即如果斷電,儲存DRAM和SRAM儲存的資訊就會丟失。

我們使用的更多的是使用磁碟,磁碟能夠儲存大量的資料,從GB一直到TB級,但是 他的讀取速度比較慢,因為涉及到機器操作,讀取速度為毫秒級,從DRAM讀速度比從磁碟度快10萬倍,從SRAM讀速度比從磁碟讀快100萬倍。下面來看下磁碟的結構:


如上圖,磁碟由碟片構成,每個碟片有兩面,又稱為盤面(Surface),這些盤面覆蓋有磁性材料。碟片中央有一個可以旋轉的主軸(spindle),他使得碟片以固定的旋轉速率旋轉,通常是5400轉每分鐘(Revolution Per Minute,RPM)或者是7200RPM。磁碟包含一個多多個這樣的碟片並封裝在一個密封的容器內。上圖左,展示了一個典型的磁碟表面結構。每個表面是由一組成為磁軌(track)的同心圓組成的,每個磁軌被劃分為了一組扇區(sector).每個扇區包含相等數量的資料位,通常是(512)子節。扇區之間由一些間隔(gap)隔開,不儲存資料。

以上是磁碟的物理結構,現在來看下磁碟的讀寫操作:


如上圖,磁碟用讀/寫頭來讀寫儲存在磁性表面的位,而讀寫頭連線到一個傳動臂的一端。通過沿著半徑軸前後移動傳動臂,驅動器可以將讀寫頭定位到任何磁軌上,這稱之為尋道操作。一旦定位到磁軌後,碟片轉動,磁軌上的每個位經過磁頭時,讀寫磁頭就可以感知到位的值,也可以修改值。對磁碟的訪問時間分為 尋道時間,旋轉時間,以及傳送時間。

由於儲存介質的特性,磁碟本身存取就比主存慢很多,再加上機械運動耗費,因此為了提高效率,要儘量減少磁碟I/O,減少讀寫操作。為了達到這個目的,磁碟往往不是嚴格按需讀取,而是每次都會預讀,即使只需要一個位元組,磁碟也會從這個位置開始,順序向後讀取一定長度的資料放入記憶體。這樣做的理論依據是電腦科學中著名的區域性性原理:

當一個數據被用到時,其附近的資料也通常會馬上被使用。

程式執行期間所需要的資料通常比較集中。                                       

由於磁碟順序讀取的效率很高(不需要尋道時間,只需很少的旋轉時間),因此對於具有區域性性的程式來說,預讀可以提高I/O效率。

預讀的長度一般為頁(page)的整倍數。頁是計算機管理儲存器的邏輯塊,硬體及作業系統往往將主存和磁碟儲存區分割為連續的大小相等的塊,每個儲存塊稱為一頁(在許多作業系統中,頁得大小通常為4k),主存和磁碟以頁為單位交換資料。當程式要讀取的資料不在主存中時,會觸發一個缺頁異常,此時系統會向磁碟發出讀盤訊號,磁碟會找到資料的起始位置並向後連續讀取一頁或幾頁載入記憶體中,然後異常返回,程式繼續執行。

檔案系統及資料庫系統的設計者利用了磁碟預讀原理,將一個節點的大小設為等於一個頁,這樣每個節點只需要一次I/O就可以完全載入。為了達到這個目的,在實際實現B-Tree還需要使用如下技巧:

每次新建一個節點的同時,直接申請一個頁的空間( 512或者1024),這樣就保證一個節點物理上也儲存在一個頁裡,加之計算機儲存分配都是按頁對齊的,就實現了一個node只需一次I/O。如,將B樹的度M設定為1024,這樣在前面的例子中,600億個元素中只需要小於4次查詢即可定位到某一儲存位置。

同時在B+樹中,內節點只儲存導航用到的key,並不儲存具體值,這樣內節點個數較少,能夠全部讀取到主存中,外接點儲存key及值,並且順序排列,具有良好的空間區域性性。所以B及B+樹比較適合與檔案系統的資料結構。另外B/B+樹也經常用做資料庫的索引,這方面推薦您直接看張洋的MySQL索引背後的資料結構及演算法原理 這篇文章,這篇文章對MySQL中的如何使用B+樹進行索引有比較詳細的介紹,推薦閱讀。

1.3. B-樹 

1.3.1.  什麼是B-樹

具體講解之前,有一點,再次強調下:B-樹,即為B樹。因為B樹的原英文名稱為B-tree,而國內很多人喜歡把B-tree譯作B-樹,其實,這是個非常不好的直譯,很容易讓人產生誤解。如人們可能會以為B-樹是一種樹,而B樹又是一種樹。而事實上是,B-tree就是指的B

從上面背景知識介紹中我們知道,B-tree 與一般的平衡樹如 AVL-Tree,Red-Black Tree 的一個顯著區別是 B-tree 的每個內結點可以擁有很多個 Children(這個度量被稱為「內結點出度」,下文會更深入的討論之),那麼我們可以在技術上使 B-tree 的結點大小為磁碟一個頁的大小,並且在新建結點時直接申請一個頁大小的空間,使得結點的物理儲存位置也是在一個頁裡,這樣就能實現存取一個結點只需一次磁碟 I/O。在最壞情況下,B-tree 的一次檢索最多需要 (樹的高度)次的磁碟 I/O。記 為B-tree 中的 Key 的資料量, 為內結點出度的二分之一,則我們可以證明 ,漸進複雜度為 。這意味著,一棵擁有 200 萬 Key 的 B-tree,在內結點出度為 200 時它的樹高 最多為 3。實際上,為了取得更大的內結點出度,各個資料庫一般會採用 B-tree的變種如 B+-tree,B*-tree來實現索引,比如 MySQL 的儲存引擎 InnoDB 就採用 B+-tree 來實現聚簇索引。

如下圖所示,即是一棵B樹,一棵關鍵字為英語中子音字母的B樹,現在要從樹中查詢字母R(包含n[x]個關鍵字的內結點x,x有n[x]+1]個子女(也就 是說,一個內結點x若含有n[x]個關鍵字,那麼x將含有n[x]+1個子女)。所有的葉結點都處於相同的深度,下圖中淺色的結點為查詢字母R時要檢查的結點):

 

相信,從上圖你能輕易的看到,一個內結點x若含有n[x]個關鍵字,那麼x將含有n[x]+1個子女。如含有2個關鍵字D H的內結點有3個子女,而含有3個關鍵字Q T X的內結點有4個子女。

B樹的定義,從下文中,你將看到,或者是用階,或者是用度,如下段文字所述:

Unfortunately,the literature on B-trees is not uniform in its use of terms relating toB-Trees. (Folk & Zoellick 1992, p. 362) Bayer & McCreight (1972),Comer (1979), and others define the order of B-tree as the minimum number ofkeys in a non-root node. Folk & Zoellick (1992) points out that terminologyis ambiguous because the maximum number of keys is not clear. An order 3 B-treemight hold a maximum of 6 keys or a maximum of 7 keys. (Knuth 1998,TAOCP p.483) avoids the problem by defining the order to be maximum number of children(which is one more than the maximum number of keys).


清華大學出版社的《資料結構(C語言版)》(2007年版),編著者為嚴蔚敏,吳偉民。該教材中關於 B-tree 的定義摘錄如下:

一棵m 階的 B-樹,或為空樹,或為滿足下列特性的 m叉樹:

1)  樹中每一個結點至多有m 棵子樹;

2)  若根結點不是葉子結點,則至少有兩棵子樹;

3)  除根結點之外的所有非終端結點至少有m/2 棵子樹;

4)  所有的非終端結點中包含下列資訊資料 (n,A0,K1,A1,K2,A2,…,Kn,An), 其中:Ki(i=1,…,n) 為關鍵字,且Ki<Ki+1(i=1,…,n-1);Ai(i=0,…,n)為指向子樹根結點的指標,且指標Ai-1 所指子樹中所有結點的關鍵字均小於Ki(i=1,…,n),An 所指子樹中所有結點的關鍵字均大於Kn ,n(⌈m/2-1⌉ ≤n≤m-1)為關鍵字的個數(或n+1 為子樹個數);

5)  所有的葉子結點都出現在同一層次上,並且不帶資訊(可以看作是外部結點或查詢失敗的結點,實際上這些結點不存在,指向這些結點的指標為空)。

比如,一棵3階B-樹,m=3。它滿足: 

(1)每個結點的孩子個數小於等於3。 

(2)除根結點外,其他結點至少有=2個孩子。 

(3)根結點有兩個孩子結點。 

(4)除根結點外的所有結點的n大於等於=1,小於等於2。 

(5)所有葉結點都在同一層上。

下面看Introduction to Algorithms(《演算法導論》)一書中關於 B-tree 的定義。摘錄原書第三版中 B-tree 定義的原文如下:

A B-tree T is a rooted tree (whose root is T.root ) having the following properties:

  • Every node has the following attributes:
  • x.n , the number of keys currently stored innode x,
  • the keys x.n themselves, x.key1 ,x.key2,…,x.keyx.n , stored in non-decreasing order, so that x.key1x.key2x.keyx.n,
  • x.leaf, a Boolean value that is TRUE if x is a leaf and FALSE if x is an internal node.
  • Each internal node x also contains x.n+1  pointers x.c1,x.c2,…,x.cx.n+1 to its children. Leaf nodes have nochildren, and so their ci attributes are undefined.
  • The keys x.keyi separate the ranges of keys stored ineach sub-tree: if keyi is any key stored in the sub-tree with root x.ci, then k1≤x.key1 ≤x.key2 ≤… ≤x.keyx.n ≤x.keyx.n +1 .
  • All leaves have the same depth, which is the tree’s height h .
  • Nodes have lower and upper bounds on the number of keys they cancontain. We express these bounds in terms of a fixed integer t2  called the minimum degreeof the B-tree:
  • Every node other than the root must have at least  t-1 keys. Every internal node other than theroot thus has at leastchildren. If the tree is non-empty, theroot must have at least one key.
  • Every node may contain at most  2t-1 keys. Therefore, an internal node may have at most 2t children. We say that a node is full ifit contains exactly 2t-1 keys.

再看高德納的經典著作 The Art of Computer Programming (《計算機程式設計藝術》)關於 B-tree 的定義,原書第二版原文摘錄如下:

A B-treeof order m is a tree that satisfies the following properties:

1)     Every node has at most m children.

2)     Every node, except for theroot and the leaves, has at least m/2 children.

3)     The root has at least 2 children (unless it is a leaf).

4)     All leaves appear on the samelevel, and carry no information.

5)     A non-leaf node with k children contains k-1 keys.

(As usual, a leaf is a terminal node, one with nochildren. Since the leaves carry no information, we may regard them as externalnodes that aren't really in the tree, so that A is a pointer to a leaf.)

要補充說明的是,上述 B-tree 定義中第二條 m/2為向上取整,亦即⌈m/2⌉,這一點在原書下文中亦有說明。

最後摘錄的是論文 Organization and Maintenance of Large Ordered Indexes (下面簡稱「論文」)中的定義:

Def. 2.1. Let h0 be an integer, k a natural number. A directed tree T is in the class  t(k,h) of B-tree if T is either empty (h=0) or has the following properties:

1)      Each path from the root to any leaf has the same length h, also called the hight of T , i.e.,  h=number of nodes in path .

2)      Each node except the root and the leaves has at least k+1 sons. The root is a leaf or has at least two sons.

3)      Each node has at most 2k+1 sons.

感興趣的話,不妨參看 Wikipedia 上 B-tree 的詞條(http://en.wikipedia.org/wiki/B-tree),與以上摘錄做一個對比。下面分析上述定義之間的差異。

不難看出,《資料結構(C語言版)》的定義參考了《計算機程式設計藝術》的定義,所以它們可以歸為同一個定義。

可以看到:

  • 差異之一是,關於leaf 的定義並不統一。《計算機程式設計藝術》一書中主張 leaf 是包含資料的最底層結點的下一層,而《演算法導論》一書與「論文」則主張最底層包含資料的結點即為 leaf。實際上,關於 leaf 定義的二義性不難理解。因為 B-tree 的實現有很多種,有些實現中,葉子結點本身可能包含了關鍵字與資料的全部資訊,另外一些實現中葉子結點也許僅僅包含了指向這些資料的指標。只要能恰當表示「B-tree的最底層」這個概念,所有這些定義都是可行的。所以說 leaf 如何定義並不關係到 B-tree 的核心概念。
  • 差異之二,亦是問題中提到的疑問,關於 B-tree 的 Order,或翻譯為「度」的定義並不統一。《演算法導論》一書中定義了一個稱為「最小度數t 」的概念,表示非根的內結點至少擁有的子女數量,並且規定內結點最多擁有的子女數為 2t。《計算機程式設計藝術》一書中定義的「度 m」表示一個結點所能擁有的最大子女數,並且非根內結點至少擁有⌈m/2⌉ 個子女。這樣,非根內結點的最小子女數並不固定,而是和「度 m」的奇偶性有關。而「論文」中並沒有明確給出度的概念,而是給出了一個自然數 k,表示非根內結點至少擁有的關鍵字(key)的數量,並且最多能擁有的 key 數量為 2k,亦即最大子女數是 2k+1。

我曾經嘗試統一上述不同定義中關於「度」的概念,比如試圖將《演算法導論》中的 經過一些數學上的推演後得到《計算機程式設計藝術》中的 。這樣的嘗試得到的結果令人費解。因為按照《演算法導論》的定義推演,最簡的 B-tree 是一棵 2-3-4 tree(每個非根內結點可以有 2 個,3 個,4 個子女)。而按照《計算機程式設計》的定義推演,最簡的 B-tree 是一棵 2-3 tree(每個非根內結點可以擁有 2 個或 3 個子女)。

理解上述「費解」差異的突破口是理解設計 B-tree 的「目的」與 B-tree 的「性質」。

回顧之前總結關於 B-tree 的三個要點可以看到,設計 B-tree 的「目的」是為了減少磁碟 I/O 的次數。所有定義與實現都要為達到這個目的而考慮。B-tree 的「性質」是:作為一個數據結構,它是一棵平衡的多路查詢樹,所有葉子結點都在同一層次,擁有相同的深度。為維護這個性質,B-tree 在進行插入與刪除操作時需要在適當的時機分裂與合併結點。為使插入與刪除操作能順利進行,B-tree 要求內結點滿足至少「半滿」,至多「全滿」的性質,這樣:

1)  當一個新 key 插入一個原本「全滿」的結點使得結點需要分裂時,我們總能找到合理的分裂點,且分裂後產生的新結點滿足「半滿」。

2)  進行刪除操作時,能夠保證讓刪除後不滿足「半滿」性質的結點通過合併而滿足「半滿」。

3)  同時仍能維護整棵樹「平衡」的性質。

「度」的概念就是為了定量描述「半滿」、「全滿」這兩個性質而引進的度量(需要注意的是,「半滿」與「全滿」並不是維護 B-tree 平衡的必要條件。例如, B*-tree 要求非根內結點至少「三分之二滿」,至多「全滿」。這也意味著其插入與刪除操作相較於 B-tree 會略有差異)。

這樣,可以解釋關於「度」的定義的差異了。

前文引用各文獻時並沒有依從時間順序。歷史上,先有「論文」,再有《計算機程式設計藝術》,再有《演算法導論》。對於宣告 B-tree 正式誕生的「論文」,根據 Wikipedia 上 B-tree 詞條的描述,當時同樣存在其他 B-tree 的定義規定最少的非根內結點 key 的數量為 k,而這些定義對最大 key 數量的規定並不統一,這意味著最大key 的數量不確定,可能為 2k,也可能為2k+1 。

高德納在他的《電腦科學與藝術》一書中通過定義非根內結點的最大子女數 來避免了這個問題。但是根據《電腦科學與藝術》對 B-tree 的「原始定義」(高德納在後文中對「原始定義」做了優化),這樣的 B-tree(或者說經典的 B-tree)在進行插入刪除操作時,如果遇到一個結點需要分裂或者兩個結點需要合併,那麼在 DFS 過程中需要回溯到父結點,這意味著一次多餘的磁碟 I/O。是否有辦法去掉這一多餘的磁碟I/O?Wikipedia 中提到:

An improved algorithm supports a single pass down the tree fromthe root to the node where the insertion will take place, splitting any fullnodes encountered on the way. This prevents the need to recall the parent nodesinto memory, which may be expensive if the nodes are on secondary storage.However, to use this improved algorithm, we must be able to send one element tothe parent and split the remaining U-2elements into two legal nodes, without adding a new element.This requires U = 2L rather than U = 2L-1, which accounts for why some textbooks impose this requirementin defining B-trees.

大意為:可以找到一種改進演算法,讓插入操作的一趟 DFS過程中,遇到全滿結點就進行分裂操作,而非等到加入一個新 key導致溢位時才進行分裂操作。此時不需要重新讀取父結點到主存。然而該改進要求「全滿」與「半滿」的數量關係嚴格滿足倍數關係 U = 2L(這裡即高德納的 m)。因為我們需要保證在分裂「全滿」結點時,提取一個 key並將其上升到父結點後,剩下的結點仍然能分裂為兩個合法的新結點。

可以看到《演算法導論》中關於 B-tree 的定義正好滿足了這個要求。《演算法導論》定義的 t 是「半滿」情況下一個非根內結點的子女數,則該「半滿」結點擁有 t-1 個 key。因此「全滿」結點擁有 2t 個子女(即 2t-1個 key)。若「全滿」結點發生分裂,則在提取一個 key 後剩下 2t - 2 個 key,正好可以分裂為兩個擁有 t-1 個 key 的「半滿」結點。這樣,我們可以認為《演算法導論》實際上是對經典的 B-tree 做了一個改進,由此導致了定義上的差異。

我的理解是,B-tree 的多種定義只是形式上的約束,最終目的是為了描述這個「平衡的多路查詢樹」的本質。

1.3.2.  B-樹的演算法思想

1B-樹的查詢

B-樹的查詢過程:根據給定值查詢結點和在結點的關鍵字中進行查詢交叉進行。首先從根結點開始重複如下過程:

若比結點的第一個關鍵字小,則查詢在該結點第一個指標指向的結點進行;若等於結點中某個關鍵字,則查詢成功;若在兩個關鍵字之間,則查詢在它們之間的指標指向的結點進行;若比該結點所有關鍵字大,則查詢在該結點最後一個指標指向的結點進行;若查詢已經到達某個葉結點,則說明給定值對應的資料記錄不存在,查詢失敗。

2B-樹的插入

插入的過程分兩步完成:

1)  利用前述的B-樹的查詢演算法查詢關鍵字的插入位置。若找到,則說明該關鍵字已經存在,直接返回。否則查詢操作必失敗於某個最低層的非終端結點上。

2)  判斷該結點是否還有空位置。即判斷該結點的關鍵字總數是否滿足n<=m-1。若滿足,則說明該結點還有空位置,直接把關鍵字k插入到該結點的合適位置上。若不滿足,說明該結點己沒有空位置,需要把結點分裂成兩個。

分裂的方法是:生成一新結點。把原結點上的關鍵字和k按升序排序後,從中間位置把關鍵字(不包括中間位置的關鍵字)分成兩部分。左部分所含關鍵字放在舊結點中,右部分所含關鍵字放在新結點中,中間位置的關鍵字連同新結點的儲存位置插入到父結點中。如果父結點的關鍵字個數也超過(m-1),則要再分裂,再往上插。直至這個過程傳到根結點為止。



3B-樹的刪除

在B-樹上刪除關鍵字k的過程分兩步完成:

1)  利用前述的B-樹的查詢演算法找出該關鍵字所在的結點。然後根據k所在結點是否為葉子結點有不同的處理方法。

2)  若該結點為非葉結點,且被刪關鍵字為該結點中第i個關鍵字key[i],則可從指標son[i]所指的子樹中找出最小關鍵字Y,代替key[i]的位置,然後在葉結點中刪去Y。

因此,把在非葉結點刪除關鍵字k的問題就變成了刪除葉子結點中的關鍵字的問題了。

在B-樹葉結點上刪除一個關鍵字的方法是:首先將要刪除的關鍵字k直接從該葉子結點中刪除。然後根據不同情況分別作相應的處理,共有三種可能情況:

1)  如果被刪關鍵字所在結點的原關鍵字個數n>=ceil(m/2),說明刪去該關鍵字後該結點仍滿足B-樹的定義。這種情況最為簡單,只需從該結點中直接刪去關鍵字即可。

2)  如果被刪關鍵字所在結點的關鍵字個數n等於ceil(m/2)-1,說明刪去該關鍵字後該結點將不滿足B-樹的定義,需要調整。

調整過程為:如果其左右兄弟結點中有“多餘”的關鍵字,即與該結點相鄰的右(左)兄弟結點中的關鍵字數目大於ceil(m/2)-1。則可將右(左)兄弟結點中最小(大)關鍵字上移至雙親結點。而將雙親結點中小(大)於該上移關鍵字的關鍵字下移至被刪關鍵字所在結點中。

3)  如果左右兄弟結點中沒有“多餘”的關鍵字,即與該結點相鄰的右(左)兄弟結點中的關鍵字數目均等於ceil(m/2)-1。這種情況比較複雜。需把要刪除關鍵字的結點與其左(或右)兄弟結點以及雙親結點中分割二者的關鍵字合併成一個結點,即在刪除關鍵字後,該結點中剩餘的關鍵字加指標,加上雙親結點中的關鍵字Ki一起,合併到Ai(是雙親結點指向該刪除關鍵字結點的左(右)兄弟結點的指標)所指的兄弟結點中去。如果因此使雙親結點中關鍵字個數小於ceil(m/2)-1,則對此雙親結點做同樣處理。以致於可能直到對根結點做這樣的處理而使整個樹減少一層。

總之,設所刪關鍵字為非終端結點中的Ki,則可以指標Ai所指子樹中的最小關鍵字Y代替Ki,然後在相應結點中刪除Y。對任意關鍵字的刪除都可以轉化為對最下層關鍵字的刪除。


如圖示:

a被刪關鍵字Ki所在結點的關鍵字數目不小於ceil(m/2),則只需從結點中刪除Ki和相應指標Ai,樹的其它部分不變。


b、被刪關鍵字Ki所在結點的關鍵字數目等於ceil(m/2)-1,則需調整。調整過程如上面所述。


c、被刪關鍵字Ki所在結點和其相鄰兄弟結點中的的關鍵字數目均等於ceil(m/2)-1,假設該結點有右兄弟,且其右兄弟結點地址由其雙親結點指標Ai所指。則在刪除關鍵字之後,它所在結點的剩餘關鍵字和指標,加上雙親結點中的關鍵字Ki一起,合併到Ai所指兄弟結點中(若無右兄弟,則合併到左兄弟結點中)。如果因此使雙親結點中的關鍵字數目少於ceil(m/2)-1,則依次類推。



1.3.3.   複雜度分析

B-樹查詢包含兩種基本動作:

●在B-樹上查詢結點

●在結點中找關鍵字

由於B-樹通常儲存在磁碟上,則前一查詢操作是在磁碟上進行的,而後一查詢操作是在記憶體中進行的,即在磁碟上找到指標p 所指結點後,先將結點中的資訊讀入記憶體,然後再利用順序查詢或折半查詢查詢等於K 的關鍵字。顯然,在磁碟上進行一次查詢比在記憶體中進行一次查詢的時間消耗多得多。

因此,在磁碟上進行查詢的次數、即待查詢關鍵字所在結點在B-樹上的層次樹,是決定B樹查詢效率的首要因素。

定理:若n1m3,則對任意一棵具有n個關鍵字的mB-樹,其樹高度h至多為logt(n+1)/2+1t= ceil(m/2)。也就是說根結點到關鍵字所在結點的路徑上涉及的結點數不超過logt(n+1)/2+1。推理如下:

可按二叉平衡樹進行類似分析。首先,討論m階B樹各層上的最少結點數。

1)  第一層為根,至少一個結點,根至少有兩個孩子,因此在第二層至少有兩個結點。

2)  除根和樹葉外,其它結點至少有m/2個孩子,因此第三層至少有2*m/2個結點,在第四層至少有2*m/22個結點…

3)  那麼在第k+1層至少有2*m/2k-1個結點(因為計算B樹高度時,葉結點所在層不計算在內),每個節點含有的關鍵字為m/2⌉-1而必然有:


n2*m/2k-1-1

klogm/2(n+1)/2+1

也就是說在n個關鍵字的B樹查詢,從根節點到關鍵字所在的節點所涉及的節點數不超過:

logm/2(n+1)/2+1

這個k也就是B樹的高度。

1.4.   B+-tree

B+-tree:是應檔案系統所需而產生的一種B-tree變形樹。

一棵m階的B+樹和m階的B樹的異同點在於:

2.所有的葉子結點中包含了全部關鍵字的資訊,及指向含有這些關鍵字記錄的指標,且葉子結點本身依關鍵字的大小自小而大的順序連結。 (B的葉子節點並沒有包括全部需要查詢的資訊)

3.所有的非終端結點可以看成是索引部分,結點中僅含有其子樹根結點中最大(或最小)關鍵字。 (B的非終節點也包含需要查詢的有效資訊)

 

a)     為什麼說B+-tree比B 樹更適合實際應用中作業系統的檔案索引和資料庫索引?

1) B+-tree的磁碟讀寫代價更低

B+-tree內部結點並沒有指向關鍵字具體資訊的指標。因此其內部結點相對B更小。如果把所有同一內部結點的關鍵字存放在同一盤塊中,那麼盤塊所能容納的關鍵字數量也越多。一次性讀入記憶體中的需要查詢的關鍵字也就越多。相對來說IO讀寫次數也就降低了。

舉個例子,假設磁碟中的一個盤塊容納16bytes,而一個關鍵字2bytes,一個關鍵字具體資訊指標2bytes。一棵9B-tree(一個結點最多8個關鍵字)的內部結點需要2個盤快。而B+樹內部結點只需要1個盤快。當需要把內部結點讀入記憶體中的時候,B就比B+樹多一次盤塊查詢時間(在磁碟中就是碟片旋轉的時間)

2) B+-tree的查詢效率更加穩定

由於非終結點並不是最終指向檔案內容的結點,而只是葉子結點中關鍵字的索引。所以任何關鍵字的查詢必須走一條從根結點到葉子結點的路。所有關鍵字查詢的路徑長度相同,導致每一個數據的查詢效率相當。

1.5.  B*-tree

B*-treeB+-tree的變體,在B+樹的基礎上(所有的葉子結點中包含了全部關鍵字的資訊,及指向含有這些關鍵字記錄的指標),B*樹中非根和非葉子結點再增加指向兄弟的指標;B*樹定義了非葉子結點關鍵字個數至少為(2/3)*M,即塊的最低使用率為2/3(代替B+樹的1/2)。給出了一個簡單例項,如下圖所示:

 

B+樹的分裂:當一個結點滿時,分配一個新的結點,並將原結點中1/2的資料複製到新結點,最後在父結點中增加新結點的指標;B+樹的分裂隻影響原結點和父結點,而不會影響兄弟結點,所以它不需要指向兄弟的指標。

B*樹的分裂:當一個結點滿時,如果它的下一個兄弟結點未滿,那麼將一部分資料移到兄弟結點中,再在原結點插入關鍵字,最後修改父結點中兄弟結點的關鍵字(因為兄弟結點的關鍵字範圍改變了);如果兄弟也滿了,則在原結點與兄弟結點之間增加新結點,並各複製1/3的資料到新結點,最後在父結點增加新結點的指標。

所以,B*樹分配新結點的概率比B+樹要低,空間使用率更高;

1.6.  總結

通過以上介紹,大致將B樹,B+樹,B*樹總結如下:

B樹:有序陣列+平衡多叉樹;

B+樹:有序陣列連結串列+平衡多叉樹;

B*樹:一棵豐滿的B+樹。

在大規模資料儲存的檔案系統中,B~tree系列資料結構,起著很重要的作用,對於儲存不同的資料,節點相關的資訊也是有所不同,這裡根據自己的理解,畫的一個查詢以職工號為關鍵字,職工號為38的記錄的簡單示意圖。(這裡假設每個物理塊容納3個索引,磁碟的I/O操作的基本單位是塊(block),磁碟訪問很費時,採用B+樹有效的減少了訪問磁碟的次數。)