1. 程式人生 > >多路查詢樹:2-3樹、2-3-4樹、B樹、B+樹、B*樹、R樹

多路查詢樹:2-3樹、2-3-4樹、B樹、B+樹、B*樹、R樹

多路查詢樹

每一個結點的孩子數可以多於兩個,且每一個結點處可以儲存多個元素。由於它是查詢樹,所有元素之間存在某種特定的排序關係。

四種特殊形式:2-3樹、2-3-4樹、B樹和B+樹

2-3樹

概念:
1、2-3樹是這樣的一棵多路查詢樹:其中的每一個結點都具有兩個孩子(我們稱它為2結點)或三個孩子(我們稱它為3結點)。

2、一個2結點包含一個元素和兩個孩子(或沒有孩子),且與二叉排序樹類似,左子樹包含的元素小於該元素,右子樹包含的元素大於該元素。不過,與二叉排序樹不同的是,這個2結點要麼沒有孩子,要有就有兩個,不能只有一個孩子。

3、一個3結點包含一小一大兩個元素和三個孩子(或沒有孩子),一個3結點要麼沒有孩子,要麼具有3個孩子。如果某個3結點有孩子的話,左子樹包含小於較小元素的元素,右子樹包含大於較大元素的元素,中間子樹包含介於兩元素之間的元素。

4、並且2-3樹中所有的葉子都在同一層次上。
btree1

注意: 2-3樹複雜的地方在於新結點的插入和已有結點的刪除。每個結點可能是2結點也可能是3結點,要保證所有葉子結點在同一層次。

2-3樹的插入

對於2-3樹的插入來說,與二叉排序樹相同,插入操作一定是發生在葉子結點上。可與二叉排序樹不同的是,2-3樹插入一個元素的過程有可能對該樹的其餘結構造成連鎖反應。

2-3樹的插入分三種情況:
1、對於空樹,插入一個2結點即可。
2、插入結點到一個2結點的葉子上,由於其本身就只有一個## 標題 ##元素,所以只需要將其升級為3結點即可。
3、要往3結點中插入一個新元素,因為3結點本身已經是2-3樹的結點最大容量,已經有兩個元素,因此就需要將其拆分,且將樹中兩元素或插入元素的三者中選擇其一向上移動一層。這裡又分為三種情況:
(1)
btree2


(2)
btree3
(3)
btree4
結論: 如果2-3樹的插入的傳播效應導致了根結點的拆分,則樹的高度就會增加。

2-3樹的刪除

分三種情況:

1、所刪除元素位於一個3結點的葉子結點上,只需要在該結點處刪除該元素即可,不會影響到整棵樹的其他結點結構。
btree5
2、所刪除的元素位於一個2結點上,即要刪除的是一個只有一個元素的結點。
btree6
1)此結點的雙親是2結點,且擁有一個3結點的右孩子。
btree7
2)此結點的雙親是2結點,它的右孩子也是2結點。
btree8
3)此結點的雙親是一個3結點。
btree9
4)如果當前樹是一個滿二叉樹的情況。
btree10
3、所刪除的元素位於非葉子的分支結點。
1)
btree11
2)
btree12

2-3-4樹

2-3-4樹是2-3樹的概念擴充套件,包括了4結點的使用。一個4結點包含小中大三個元素和四個孩子(或沒有孩子),一個4結點要麼沒有孩子,要麼具有4個孩子。如果某個4結點右孩子的話,左子樹包含小於最小元素的元素;第二個子樹包含大於最小元素,小於第二元素的元素;第三字數包含書大於第二元素,小於最大元素的元素;右子樹包含大於最大元素的元素。

構建一個數組為{7,1,2,5,6,9,8,4,3}的2-3-4樹的過程:

2-3-4樹的插入:
btree13
2-3-4樹的刪除:
btree14

B樹

B樹是一種平衡的多路查詢樹,2-3樹和2-3-4樹都是B樹的特例。結點最大的孩子數目稱為B樹的階,因此2-3樹是3階B樹,2-3-4樹是4階B樹。
btree15
btree16
btree17
btree18
在B樹上查詢的過程是一個順時針查詢結點和在結點中查詢關鍵字的交叉過程。

比方說,我們要查詢數字7,首先從外存(比如硬碟中)讀取得到根結點3 、5 、8三個元素,發現7不在當中,但在5和8之間,因此就通過A2再讀取外存的6 、7結點,查詢到所耍的元素。

至於B樹的插入和刪除,方式是與2-3樹2-3-4樹相類似的,只不過階數可能會很大而已。
btree19
B樹的插入
btree20
btree21
B樹的刪除
btree22

B+樹

對於樹結構來說,我們都可以通過中序遍歷來順序查詢樹中的元素,這一切都是在記憶體中進行。
bt23
bt24
bt25
bt26

B樹和B+樹的區別

bt27
如果要隨機查詢,我們就從根結點出發,與B樹的查詢的方式不同,只不過即使在分支結點找到了待查詢的關鍵字,它也只是用來索引的,不能提供實際記錄的訪問,還是需要到達包含此關鍵字的終端結點。

如果我們是需要從最小關鍵字進行從小到大的順序查詢,我們就可以從最左側的葉子結點出發,不經過分支結點,而是延著指向下一葉子的指標就可遍歷所有的關鍵字。
bt28
bt29
bt30
B/B+樹通過對每個節點儲存個數的擴充套件,使得對連續的資料能夠進行較快的定位和訪問,能夠有效減少查詢時間,提高儲存的空間區域性性從而減少IO操作。他廣泛用於檔案系統及資料庫中,如:

Windows:HPFS檔案系統
Mac:HFS,HFS+檔案系統
Linux:ResiserFS,XFS,Ext3FS,JFS檔案系統
資料庫:ORACLE,MYSQL,SQLSERVER等中

根據B+樹的結構,我們可以發現B+樹相比於B樹,在檔案系統,資料庫系統當中,更有優勢,原因如下:

1、B+樹的磁碟讀寫代價更低
B+樹的內部結點並沒有指向關鍵字具體資訊的指標。因此其內部結點相對B樹更小。如果把所有同一內部結點的關鍵字存放在同一盤塊中,那麼盤塊所能容納的關鍵字數量也越多。一次性讀入記憶體中的需要查詢的關鍵字也就越多。相對來說I/O讀寫次數也就降低了。
舉個例子,假設磁碟中的一個盤塊容納16bytes,而一個關鍵字2bytes,一個關鍵字具體資訊指標2bytes。一棵9階B-tree(一個結點最多8個關鍵字)的內部結點需要2個盤快。而B+ 樹內部結點只需要1個盤快。當需要把內部結點讀入記憶體中的時候,B 樹就比B+ 樹多一次盤塊查詢時間(在磁碟中就是碟片旋轉的時間)。

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

2、B+樹更有利於對資料庫的掃描
B樹在提高了磁碟IO效能的同時並沒有解決元素遍歷的效率低下的問題,而B+樹只需要遍歷葉子節點就可以解決對全部關鍵字資訊的掃描,所以對於資料庫中頻繁使用的range query,B+樹有著更高的效能。

B*樹

B*-tree是B+-tree的變體,在B+樹的基礎上(所有的葉子結點中包含了全部關鍵字的資訊,及指向含有這些關鍵字記錄的指標),B*樹中非根和非葉子結點再增加指向兄弟的指標;B*樹定義了非葉子結點關鍵字個數至少為(2/3)*M,即塊的最低使用率為2/3(代替B+樹的1/2)。給出了一個簡單例項,如下圖所示:
bt31
B+樹的分裂:當一個結點滿時,分配一個新的結點,並將原結點中1/2的資料複製到新結點,最後在父結點中增加新結點的指標;B+樹的分裂隻影響原結點和父結點,而不會影響兄弟結點,所以它不需要指向兄弟的指標。

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

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

總結:
bt32
B樹是一棵平衡樹,它是把一維直線分為若干段線段,當我們查詢滿足某個要求的點的時候,只要去查詢它所屬的線段即可。依我看來,這種思想其實就是先找一個大的空間,再逐步縮小所要查詢的空間,最終在一個自己設定的最小不可分空間內找出滿足要求的解。一個典型的B樹查詢如下:
bt33

R樹

R樹在資料庫等領域做出的功績是非常顯著的。它很好的解決了在高維空間搜尋等問題。舉個R樹在現實領域中能夠解決的例子:查詢20英里以內所有的餐廳。如果沒有R樹你會怎麼解決?一般情況下我們會把餐廳的座標(x,y)分為兩個欄位存放在資料庫中,一個欄位記錄經度,另一個欄位記錄緯度。這樣的話我們就需要遍歷所有的餐廳獲取其位置資訊,然後計算是否滿足要求。如果一個地區有100家餐廳的話,我們就要進行100次位置計算操作了,如果應用到谷歌地圖這種超大資料庫中,這種方法便必定不可行了。

R樹就很好的解決了這種高維空間搜尋問題。它把B樹的思想很好的擴充套件到了多維空間,採用了B樹分割空間的思想,並在新增、刪除操作時採用合併、分解結點的方法,保證樹的平衡性。因此,R樹就是一棵用來儲存高維資料的平衡樹。