1. 程式人生 > >B樹與B+樹學習筆記

B樹與B+樹學習筆記

一般情況下,我們所討論的資料結構,都是處理在記憶體中的資料。因此考慮的都是記憶體中的運算時間複雜度。

如果我們要操作的資料集非常大,達到記憶體已經沒辦法處理了怎麼辦呢?如資料庫中的上千萬條記錄的資料表。硬碟中的上萬個檔案等。在這種情況下,對資料的處理需要不斷從硬碟燈儲存裝置中調入或調出記憶體頁面(頁面排程演算法)。

一旦涉及到這樣的外部儲存裝置。關於時間複雜度的計算就會發生變化,訪問該集合元素的時間已經不僅僅是尋找該元素所需比較次數的函式,我們必須考慮對硬碟等外部儲存裝置的訪問時間以及將會對該裝置做出多少次單獨訪問。在這樣的一個場景中,為了要在一個擁有幾十萬個檔案的磁碟中查詢一個文字檔案,你設計的演算法需要讀取磁碟上萬次還是讀取幾十次即可,這是由本質區別的。為了降低對外存裝置的訪問次數,我們需要新的資料結構來處理這種問題。

這裡我們引入多路查詢樹的概念。其每一個節點的孩子數可以多於兩個,且每一個節點處都可以儲存多個元素。由於它是查詢樹,所有元素之間存在某種特定的排序關係。下面我們介紹一下B樹和B+樹。

B樹是一種平衡的多路查詢樹,節點最大的孩子數目稱為B樹的階。

一個m階的B樹具有如下屬性:

(1)如果根節點不是葉節點,則至少有兩棵子樹。

(2)每一個非根的分支節點都有k-1個元素和k個孩子,其中[m/2]<=k<=m。每一個葉子結點n都有k-1個元素,其中[m/2]<=k<=m

(3)所有葉子結點都位於同一層次。

在B樹上查詢的過程是一個順指標查詢節點和在節點中查詢關鍵字的交叉過程。比如說,我們要查詢數字7,首先從外存讀取得到根節點3,5,8三個元素,發現7不在當中,但是7在5與8之間,因此可以通過指標讀取外村的6,7節點,查詢到所需要的元素。

如果記憶體與外存交換資料次數頻繁,會造成時間效率的瓶頸,那麼B樹結構怎麼就可以做到減少次數呢。

我們的外存,比如硬碟,是將所有的資訊分割成相等大小的頁面,每次磁碟讀寫的都是一個或多個完整的頁面,對於一個硬碟來說,一頁的長度可能是211到214個位元組。

再一個典型的B樹應用中,要處理的硬碟資料量很大,因此無法一次全部裝入記憶體,因此我們會對B樹進行調整,使得B樹的階數與硬碟儲存的頁面大小相匹配。比如一個B樹的階為1001(1個節點包含1000個關鍵字作為子樹),高度為2,他可以儲存超過10億個關鍵字,我們只要讓根節點持久的保持在記憶體中,那麼在這棵樹中,尋找某一個關鍵字之多需要兩次硬碟的讀取即可。

通過這種方式,在有限記憶體的情況下,每一次磁碟的訪問我們都可以獲得最大數量的資料。由於B樹每結點可以具有比二叉樹多得多的元素,所以與二叉樹的操作不同,他們減少了必須訪問結點和資料塊的數量,從而提高了效能。可以說,B樹的資料結構就是為內外村的資料互動準備的。

前面講了B樹的諸多好處,其實他還是有缺陷的,對於樹形結構,我們可以通過中序遍歷來順序查詢樹中的元素,這一切都是在記憶體中進行的。

可在B樹中,我們往返於每個節點之間也就意味著,我們必須得在硬碟的頁面之間進行多次訪問,如下圖所示(陰影部分表示節點內元素個數):我們希望遍歷這棵B樹,假設每個節點都屬於硬碟的不同頁面,我們為了中序遍歷所有元素,頁面2->頁面1->頁面3->頁面1->頁面4->頁面1->頁面5.而且我們每次經過節點遍歷是,都會對節點中的元素進行一次遍歷,這樣會非常糟糕,所以我們需要進行相應的改進。

為了能夠解決所有元素遍歷等基本問題,我們在原有的B樹結構基礎上,加上了新的元素組織方式,這就是B+樹,B+樹是應檔案系統所需而出的一種B樹的變形樹,注意嚴格意義上講,它其實已經不是一棵樹了。在B樹中,每一個元素在該樹中只出現一次,有可能在葉子節點上,也有可能在分支節點上,而在B+樹中,出現在分支節點的元素會被當做他們在該分支節點位置的中序後繼者(葉子結點)中再次列出,另外,每一個葉子結點都會儲存一個指向後一葉子結點的指標。

如下圖所示,就是一個B+樹事宜,灰色關鍵字是根節點中的關鍵字在葉子結點中再次列出,並且所有葉子結點都連線在一起。

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

(1)有n棵子樹的節點中包含有n個關鍵字

(2)所有的葉子結點包含全部關鍵字的資訊,及指向含這些關鍵字記錄的指標,葉子結點本身依關鍵字的大小自小而大順序連線。

(3)所有分治節點都可以看成是索引,節點中僅含有其子樹中的最大(或最小)關鍵字。

這樣的資料結構最大的好處在於,如果要隨機查詢,我們就可以從根節點出發,與B樹的查詢方式相同,只不過即使在分支節點找到了待查詢的關鍵字,它也只是用來索引的,不能提供實際記錄的訪問,而是需要到達包含此關鍵字的終端節點。

如果我們是需要從最小關鍵字進行順序超找,我們可以從最左側的葉子結點出發,不經過分支節點,按照連結串列中的查詢方式進行查詢。