1. 程式人生 > >專注於資料探勘演算法研究和應用

專注於資料探勘演算法研究和應用

開宗明義,B樹是為磁碟或其他直接存取輔助裝置而設計的一種平衡查詢樹。一般設計的簡單資料結構都是面向主存而設計的,主存讀取速度快但容量小;而磁碟讀取速度慢而容量大,於是針對磁碟而設計的資料結構就不同於為主存而設計的。就樹結構上來說,紅黑樹的二叉性質和高深度適合主存,而B樹正是應磁碟特點而設計的高階樹結構,其高度比紅黑樹小很多,但廣度要大很多,其分支不只2個,分支因子越大,高度越小。

在考察演算法效能時,需要考慮兩方面:磁碟存取次數和CPU執行時間。主存儲存量小,一旦要處理的資料量比較龐大,就需要以頁在磁碟和主存之間交換,選擇頁複製到主存中操作再將修改過的頁寫回磁碟,這來回就是磁碟存取消耗的效能,因此交換頁在作業系統磁碟模組是一個很重要的演算法。

B樹設計上,一個結點的大小相當於一個完整的磁碟頁,其子女數就由磁碟頁的大小來決定。這樣一個節點的讀取就交換一個頁,減少磁碟IO次數。下面定義B樹:

一棵B樹T是具有如下性質的有根樹(根為root[T]):

1)每個結點x有以下域構成:

   a)n[x],當前儲存在結點x中的關鍵字數;

   b)n[x]個關鍵字本身,以非降序存放,key1[x]=<key2[x]=<…=<keyn[x][x]

   c)leaf[x],布林值,如果x是葉子該值為true,如果x是內結點則為false;

2)每個內結點x還包含n[x]+1個指向其子女的指標c1[x],c1[x],…,cn[x]+1

[x],葉結點沒有子女,故而ci域沒有定義;

3)各關鍵字keyi[x]對儲存在各子樹中的關鍵字範圍加以分割,如果ki為儲存在以ci[x]為根的子樹中的關鍵字,那麼:

k1=<key1[x]=< k2=< key2[x]=<…=< kn[x]=<keyn[x]+1[x]

子節點的值是被父結點的值區隔開。

4)每個葉結點具有相同深度,即樹的高度h;

5)每一個節點能包含的關鍵字數有一個上界和下界,界可用一個B樹的最小度數的固定整數t>=2來表示;

    a)每個非根的結點必須至少有t-1個關鍵字,每個非根的內結點至少有t個子女,如果樹是非空的,則根節點至少包含一個關鍵字;

    b)每個節點可包含至多2t-1個關鍵字,故一個內結點至多可有2t個子女,如果一個結點是滿的,那就有2t-1個關鍵字。

如果t=2,就是每個非根結點的關鍵字數為2,其每個內結點有2個、3個或4個子女,是一顆2-3-4樹。t其實就是限制了每個結點的關鍵字數,在t-1和2t-1範圍內,取閉區間。

B樹設計上一般考慮一個結點大小和一個磁碟頁相當,因此磁碟存取次數和B樹高度成正比。通過B樹最壞情況的高度來衡量效能。如果n>=1,對任意一顆包含n個關鍵字、高度為h、最小度數t>=2的B樹T,則:


定義了B樹和分析其效能,下面對B樹的基本操作進行描述。

B樹的基本操作和二叉樹類似,不同的是每個結點不只是二叉決定,而是根據B樹的度(結點關鍵字數)做決定。B樹的每個內結點x,都需要做n[x]+1路的分支決定。

1)B樹的搜尋操作

從根結點出發搜尋關鍵字k,在演算法上只需依次從頂層定位關鍵字值序的空間就可以。關注下,B樹搜尋操作的複雜度:搜尋是從樹根一直下降的過程,需存取的磁碟頁面數為樹的高度就是:O(h)=O(logtn),t是節點的度,n是關鍵字數,h是樹高度;對於CPU執行時間消耗來說,每個節點的迴圈式為O(t),總共進行h次迴圈,那總的時間就是O(th)=O(tlogtn)。

2)B樹的插入操作

B樹插入操作比較複雜,必須對滿結點進行分裂操作,以維持B樹結點至多2t-1個關鍵字的性質。從根節點出發,尋找待插入關鍵字的序值位置,如果結點滿,則分裂,將中間值插入到父節點,如果父節點也因此滿,需要進一步分裂,遞迴如是。文中有一個思路很好,就是在尋找關鍵字要插入的結點過程中,在查詢沿途過程中發現有滿節點(包含葉結點本身)就分類,而不是等最後插入時發現了才分裂。看看滿節點分裂演算法的描述:

Fun_Btree_split_child(x,i,y){//x是父結點,y是子節點,i是x的分裂點

    Allocat_node(z);//分配一個z結點

    Leaf[z]=leaf[y];//把y中t個最大關鍵字(包含其t+1個子女)複製給z

    n[z]=t-1;

    for j=1 to t-1

        do keyj[z]=keyj+t[y];

    if not leaf[y] //y有子女

        then for j=1 to t

            do cj[z]=cj+t[y]

    n[y]=t-1;

//到此已經將y節點,從中間分類成兩個節點,y和z,其中z是較大關鍵字的那部分

//下面就是將y的中間關鍵字提升到父結點x中,左右分別指向y和z

for j=n[x]+1downto i+1

        do cj+1[x]=cj[x];//x的子女向右移動一個位置

    ci+1[x]=z;

    for j=n[x] downto i

        do keyj+1[x]=keyj[x];//x的關鍵字向右移動一個位置

    keyi[x]=keyt[y]

    n[x]=n[x+1]

    Disk-Write(y);

    Disk-Write(z);

Disk-Write(x);//回寫磁碟,一個結點一頁

}

有了分裂函式,插入演算法就不描述了,在遇到滿結點時呼叫分裂函式即可。對高度為h的B樹,插入的磁碟存取次數是O(h),cpu執行時間是O(th)=O(tlogtn)。

3)B數的刪除操作

B樹刪除操作和插入一樣麻煩,需要確保刪除後結點數不小於t-1個關鍵字的性質。刪除的演算法思路就是合併節點,複雜度和插入一樣。這裡不具體描述。

B樹的應用是很廣泛的,對於演算法複雜度來說,關注CPU時間是不夠的,因為多數情況,需要整體考慮裝置效能,而磁碟IO能力是一個很關鍵但常常是制約效能的指標。