B樹(shù)、B+樹(shù)、B*樹(shù)

B-樹(shù),就是B樹(shù),B樹(shù)的原英文名是B-tree,所以很多翻譯為B-樹(shù),就會(huì)很多人誤以為B-樹(shù)是一種樹(shù)、B樹(shù)是另外一種樹(shù)。其實(shí),B-tree就是B樹(shù)。

B樹(shù)是一種多叉平衡查找樹(shù),我們之前所介紹的紅黑樹(shù)是二叉查找樹(shù)結(jié)構(gòu),B樹(shù)由于是多叉結(jié)構(gòu),對(duì)于元素?cái)?shù)量非常多的情況下,樹(shù)的深度不會(huì)像二叉結(jié)構(gòu)那么大,可以保證查詢(xún)效率。

B樹(shù)的性質(zhì)(m階的B樹(shù))

  1. 樹(shù)中每個(gè)結(jié)點(diǎn)最多含有m個(gè)孩子(m>=2);
  2. 除根結(jié)點(diǎn)和葉子結(jié)點(diǎn)外,其它每個(gè)結(jié)點(diǎn)至少有[ceil(m / 2)]個(gè)孩子(其中ceil(x)是一個(gè)取上限的函數(shù));
  3. 根結(jié)點(diǎn)至少有2個(gè)孩子(除非B樹(shù)只包含一個(gè)結(jié)點(diǎn):根結(jié)點(diǎn));
  4. 所有葉子結(jié)點(diǎn)都出現(xiàn)在同一層,葉子結(jié)點(diǎn)不包含任何關(guān)鍵字信息(可以看做是外部結(jié)點(diǎn)或查詢(xún)失敗的結(jié)點(diǎn),指向這些結(jié)點(diǎn)的指針都為null);(注:葉子節(jié)點(diǎn)只是沒(méi)有孩子和指向孩子的指針,這些節(jié)點(diǎn)也存在,也有元素。類(lèi)似紅黑樹(shù)中,每一個(gè)NULL指針即當(dāng)做葉子結(jié)點(diǎn),只是沒(méi)畫(huà)出來(lái)而已)。
  5. 每個(gè)非終端結(jié)點(diǎn)中包含有n個(gè)關(guān)鍵字信息: (n,P0,K1,P1,K2,P2,......,Kn,Pn)。其中:
    a) Ki (i=1...n)為關(guān)鍵字,且關(guān)鍵字按順序升序排序K(i-1)< Ki。
    b) Pi為指向子樹(shù)根的結(jié)點(diǎn),且指針P(i-1)指向子樹(shù)種所有結(jié)點(diǎn)的關(guān)鍵字均小于Ki,但都大于K(i-1)。
    c) 關(guān)鍵字的個(gè)數(shù)n必須滿(mǎn)足: [ceil(m / 2)-1]<= n <= m-1。比如有j個(gè)孩子的非葉結(jié)點(diǎn)恰好有j-1個(gè)關(guān)鍵碼。

B樹(shù)的插入
根據(jù)B樹(shù)的性質(zhì),一個(gè)m階的B樹(shù)需要滿(mǎn)足:

  • 樹(shù)中每個(gè)結(jié)點(diǎn)含有最多含有m個(gè)孩子,即m滿(mǎn)足:ceil(m/2)<=m<=m。
  • 除根結(jié)點(diǎn)和葉子結(jié)點(diǎn)外,其它每個(gè)結(jié)點(diǎn)至少有[ceil(m / 2)]個(gè)孩子(其中ceil(x)是一個(gè)取上限的函數(shù));
  • 除根結(jié)點(diǎn)之外的結(jié)點(diǎn)的關(guān)鍵字的個(gè)數(shù)n必須滿(mǎn)足: [ceil(m / 2)-1]<= n <= m-1(葉子結(jié)點(diǎn)也必須滿(mǎn)足此條關(guān)于關(guān)鍵字?jǐn)?shù)的性質(zhì))。

針對(duì)一棵高度為h的m階B樹(shù),插入一個(gè)元素時(shí),首先在B樹(shù)中是否存在,如果不存在,一般在葉子結(jié)點(diǎn)中插入該新的元素,此時(shí)分3種情況:

  • 如果葉子結(jié)點(diǎn)空間足夠,即該結(jié)點(diǎn)的關(guān)鍵字?jǐn)?shù)小于m-1,則直接插入在葉子結(jié)點(diǎn)的左邊或右邊;

  • 如果空間滿(mǎn)了以致沒(méi)有足夠的空間去添加新的元素,即該結(jié)點(diǎn)的關(guān)鍵字?jǐn)?shù)已經(jīng)有了m個(gè),則需要將該結(jié)點(diǎn)進(jìn)行“分裂”,將一半數(shù)量的關(guān)鍵字元素分裂到新的其相鄰右結(jié)點(diǎn)中,中間關(guān)鍵字元素上移到父結(jié)點(diǎn)中,而且當(dāng)結(jié)點(diǎn)中關(guān)鍵元素向右移動(dòng)了,相關(guān)的指針也需要向右移。

  • 此外,如果在上述中間關(guān)鍵字上移到父結(jié)點(diǎn)的過(guò)程中,導(dǎo)致根結(jié)點(diǎn)空間滿(mǎn)了,那么根結(jié)點(diǎn)也要進(jìn)行分裂操作,這樣原來(lái)的根結(jié)點(diǎn)中的中間關(guān)鍵字元素向上移動(dòng)到新的根結(jié)點(diǎn)中,因此導(dǎo)致樹(shù)的高度增加一層。

插入以下字符字母到一棵空的5階B 樹(shù)中:C N G A H E K Q M F W L T Z D P R X Y S
分析: 根據(jù)上面的性質(zhì)總結(jié),5階的B樹(shù),非根節(jié)點(diǎn)關(guān)鍵字個(gè)數(shù)n滿(mǎn)足2<=n<=4,每個(gè)節(jié)點(diǎn)最多含有5個(gè)孩子,除根節(jié)點(diǎn)葉子節(jié)點(diǎn)之外,其他節(jié)點(diǎn)至少3個(gè)孩子。

  1. 關(guān)鍵字個(gè)數(shù)最大4,先取前4個(gè)插入到相同的節(jié)點(diǎn)中。


    1.jpg
  2. 插入H,因?yàn)椴襟E一后空間不夠,就需要將中間關(guān)鍵字元素上移到父結(jié)點(diǎn)中,樹(shù)增加一層


    2.jpg
  3. 在步驟二的圖中,可以繼續(xù)插入E,K,Q三個(gè)節(jié)點(diǎn),繼續(xù)插就得分裂


    3.jpg
  4. 插入M將進(jìn)行分裂,M剛好是中間元素,直接上移到父節(jié)點(diǎn)中,HK、NQ分開(kāi)為兩個(gè)節(jié)點(diǎn)


    4.jpg
  5. 如步驟四的圖中可以繼續(xù)插入F,W,L,T


    5.jpg
  6. 在步驟五之后,插入Z就得進(jìn)行分裂,T上移到父節(jié)點(diǎn)


    6.jpg
  7. 如步驟六的圖中插入D,進(jìn)行分裂,D上移到父節(jié)點(diǎn)中,然后插入后續(xù)的P,R,X,Y節(jié)點(diǎn)沒(méi)有分裂


    7.jpg
  8. 插入最后一個(gè)S,含有N,P,Q,R的節(jié)點(diǎn)需要分裂,Q上移,導(dǎo)致父節(jié)點(diǎn)D,G,M,T也滿(mǎn)了,也需要進(jìn)行分裂,繼續(xù)將中間元素M上移,產(chǎn)生新的節(jié)點(diǎn),樹(shù)高度再加一層。


    8.jpg

B樹(shù)的刪除
首先查找B樹(shù)中要?jiǎng)h除的元素,若元素存在,則進(jìn)行刪除。刪除該元素后,需要判斷該元素是否有左右孩子節(jié)點(diǎn)

  • 如果有,則上移孩子節(jié)點(diǎn)中的相近元素(左孩子中最右邊的節(jié)點(diǎn)或者右孩子中最左邊的節(jié)點(diǎn))到父節(jié)點(diǎn)中去,移動(dòng)之后的情況。
  • 如果沒(méi)有,直接刪除,移動(dòng)之后的情況。

刪除元素,然后進(jìn)行元素移動(dòng)之后,如果節(jié)點(diǎn)關(guān)鍵字?jǐn)?shù)目不滿(mǎn)足條件(小于ceil(m/2)-1),則需要看其相鄰的兄弟節(jié)點(diǎn)是否豐滿(mǎn)(關(guān)鍵字個(gè)數(shù)大于ceil(m/2)-1)

  • 如果豐滿(mǎn),則向父節(jié)點(diǎn)借一個(gè)元素來(lái)滿(mǎn)足
  • 如果其相鄰兄弟都剛脫貧,即借了之后其結(jié)點(diǎn)數(shù)目小于ceil(m/2)-1,則該結(jié)點(diǎn)與其相鄰的某一兄弟結(jié)點(diǎn)進(jìn)行“合并”成一個(gè)結(jié)點(diǎn),以此來(lái)滿(mǎn)足條件。

對(duì)剛剛插入的樹(shù)進(jìn)行刪除操作,依次刪除H,T,R,E

  1. 刪除H,在葉子節(jié)點(diǎn)H,K,L中,刪除后還剩兩個(gè)關(guān)鍵字,能夠滿(mǎn)足不小于ceil(m/2)-1=2,進(jìn)行簡(jiǎn)單的刪除元素后面的元素向前移動(dòng)即可。


    d1.jpg
  2. 刪除T,QT節(jié)點(diǎn)不滿(mǎn)足關(guān)鍵字要求,需要上移孩子節(jié)點(diǎn)中相近元素W


    d2.jpg
  3. 刪除R,刪除后RS節(jié)點(diǎn)只剩一個(gè)關(guān)鍵字,根據(jù)上面的分析,兄弟節(jié)點(diǎn)豐滿(mǎn),就向父節(jié)點(diǎn)借一個(gè)W,同時(shí)X需要上移到父節(jié)點(diǎn)中去。


    d3.jpg
  4. 刪除E,刪除后EF節(jié)點(diǎn)只剩一個(gè)關(guān)鍵字,根據(jù)上面分析,兄弟節(jié)點(diǎn)剛脫貧,則需要跟相鄰兄弟節(jié)點(diǎn)合并,D在兩個(gè)需要合并的節(jié)點(diǎn)之間,所以需要下移到之前的AC節(jié)點(diǎn)中,將僅剩的F進(jìn)行合并,形成ACDF節(jié)點(diǎn)


    d4.jpg

    但是我們發(fā)現(xiàn)中間有一個(gè)節(jié)點(diǎn)只包含一個(gè)關(guān)鍵字,并且該節(jié)點(diǎn)非根節(jié)點(diǎn),這個(gè)就需要進(jìn)行修改。接下來(lái)進(jìn)行分析:如果相鄰兄弟節(jié)點(diǎn)豐滿(mǎn),可以從父節(jié)點(diǎn)中進(jìn)行借一個(gè)元素,但是我們右邊的QX節(jié)點(diǎn)并不豐滿(mǎn),所以只能下移M節(jié)點(diǎn),減少樹(shù)的高度。最終圖如下:


    d5.jpg

B+樹(shù)
B樹(shù)的一種變形樹(shù),m階的B+樹(shù)和m階的B樹(shù)區(qū)別:

  1. 所有葉子節(jié)點(diǎn)包含全部關(guān)鍵字信息,及指向含有這些關(guān)鍵字記錄的指針,且葉子節(jié)點(diǎn)中關(guān)鍵字進(jìn)行有序鏈接
  2. 非葉子結(jié)點(diǎn)相當(dāng)于是葉子結(jié)點(diǎn)的索引(稀疏索引),葉子結(jié)點(diǎn)相當(dāng)于是存儲(chǔ)(關(guān)鍵字)數(shù)據(jù)的數(shù)據(jù)層;
B+樹(shù).jpg

B+樹(shù)比B樹(shù)更適合操作系統(tǒng)的文件索引和數(shù)據(jù)庫(kù)索引的原因:

  • B+樹(shù)的磁盤(pán)讀寫(xiě)代價(jià)更低,B+樹(shù)的內(nèi)部節(jié)點(diǎn)沒(méi)有指向關(guān)鍵字具體信息的指針,因此內(nèi)部節(jié)點(diǎn)相對(duì)B樹(shù)更小。如果把所有同一內(nèi)部節(jié)點(diǎn)的關(guān)鍵字放在同一塊磁盤(pán)中,盤(pán)塊所能容納的關(guān)鍵字?jǐn)?shù)量也就越多,一次性讀入內(nèi)存中的需要查找的關(guān)鍵字也就越多,相對(duì)IO讀寫(xiě)次數(shù)降低

舉個(gè)例子,假設(shè)磁盤(pán)中的一個(gè)盤(pán)塊容納16bytes,而一個(gè)關(guān)鍵字2bytes,一個(gè)關(guān)鍵字具體信息指針2bytes。一棵9階B-tree(一個(gè)結(jié)點(diǎn)最多8個(gè)關(guān)鍵字)的內(nèi)部結(jié)點(diǎn)需要2個(gè)盤(pán)塊。而B(niǎo)+
樹(shù)內(nèi)部結(jié)點(diǎn)只需要1個(gè)盤(pán)快。當(dāng)需要把內(nèi)部結(jié)點(diǎn)讀入內(nèi)存中的時(shí)候,B 樹(shù)就比B+ 樹(shù)多一次盤(pán)塊查找時(shí)間(在磁盤(pán)中就是盤(pán)片旋轉(zhuǎn)的時(shí)間)。

  • B+樹(shù)的查詢(xún)效率更加穩(wěn)定
    由于非終結(jié)點(diǎn)并不是最終指向文件內(nèi)容的結(jié)點(diǎn),而只是葉子結(jié)點(diǎn)中關(guān)鍵字的索引。所以任何關(guān)鍵字的查找必須走一條從根結(jié)點(diǎn)到葉子結(jié)點(diǎn)的路。所有關(guān)鍵字查詢(xún)的路徑長(zhǎng)度相同,導(dǎo)致每一個(gè)數(shù)據(jù)的查詢(xún)效率相當(dāng)。

總而言之,B樹(shù)在提高了磁盤(pán)IO性能的同時(shí)并沒(méi)有解決元素遍歷的效率低下的問(wèn)題。正是為了解決這個(gè)問(wèn)題,B+樹(shù)應(yīng)運(yùn)而生。B+樹(shù)只要遍歷葉子節(jié)點(diǎn)就可以實(shí)現(xiàn)整棵樹(shù)的遍歷,支持基于范圍的查詢(xún),而B(niǎo)樹(shù)不支持range-query這樣的操作(或者說(shuō)效率太低)。

B樹(shù)*
B*樹(shù)是B+樹(shù)的變體,在B+樹(shù)的非根和非葉子結(jié)點(diǎn)再增加指向兄弟的指針;

B*樹(shù).jpg

B+樹(shù)的分裂:當(dāng)一個(gè)結(jié)點(diǎn)滿(mǎn)時(shí),分配一個(gè)新的結(jié)點(diǎn),并將原結(jié)點(diǎn)中1/2的數(shù)據(jù)復(fù)制到新結(jié)點(diǎn),最后在父結(jié)點(diǎn)中增加新結(jié)點(diǎn)的指針;B+樹(shù)的分裂只影響原結(jié)點(diǎn)和父結(jié)點(diǎn),而不會(huì)影響兄弟結(jié)點(diǎn),所以它不需要指向兄弟的指針。

B*樹(shù)的分裂:當(dāng)一個(gè)結(jié)點(diǎn)滿(mǎn)時(shí),如果它的下一個(gè)兄弟結(jié)點(diǎn)未滿(mǎn),那么將一部分?jǐn)?shù)據(jù)移到兄弟結(jié)點(diǎn)中,再在原結(jié)點(diǎn)插入關(guān)鍵字,最后修改父結(jié)點(diǎn)中兄弟結(jié)點(diǎn)的關(guān)鍵字(因?yàn)樾值芙Y(jié)點(diǎn)的關(guān)鍵字范圍改變了);如果兄弟也滿(mǎn)了,則在原結(jié)點(diǎn)與兄弟結(jié)點(diǎn)之間增加新結(jié)點(diǎn),并各復(fù)制1/3的數(shù)據(jù)到新結(jié)點(diǎn),最后在父結(jié)點(diǎn)增加新結(jié)點(diǎn)的指針。

總結(jié)

  • B-樹(shù):多路搜索樹(shù),每個(gè)結(jié)點(diǎn)存儲(chǔ)M/2到M個(gè)關(guān)鍵字,非葉子結(jié)點(diǎn)存儲(chǔ)指向關(guān)鍵字范圍的子結(jié)點(diǎn);所有關(guān)鍵字在整顆樹(shù)中出現(xiàn),且只出現(xiàn)一次,非葉子結(jié)點(diǎn)可以命中;

  • B+樹(shù):在B-樹(shù)基礎(chǔ)上,為葉子結(jié)點(diǎn)增加鏈表指針,所有關(guān)鍵字都在葉子結(jié)點(diǎn) 中出現(xiàn),非葉子結(jié)點(diǎn)作為葉子結(jié)點(diǎn)的索引;B+樹(shù)總是到葉子結(jié)點(diǎn)才命中;

  • B*樹(shù):在B+樹(shù)基礎(chǔ)上,為非葉子結(jié)點(diǎn)也增加鏈表指針,將結(jié)點(diǎn)的最低利用率從1/2提高到2/3;

借鑒于July大神的分析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,908評(píng)論 6 541
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,324評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 178,018評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,675評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,417評(píng)論 6 412
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,783評(píng)論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,779評(píng)論 3 446
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,960評(píng)論 0 290
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,522評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,267評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,471評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,009評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,698評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 35,099評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,386評(píng)論 1 294
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,204評(píng)論 3 398
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,436評(píng)論 2 378

推薦閱讀更多精彩內(nèi)容