前言
推出一個新系列,《看圖輕鬆理解資料結構和演算法》,主要使用圖片來描述常見的資料結構和演算法,輕鬆閱讀並理解掌握。本系列包括各種堆、各種佇列、各種列表、各種樹、各種圖、各種排序等等幾十篇的樣子。
B樹
B樹即平衡查詢樹,一般理解為平衡多路查詢樹,也稱為B-樹、B_樹。是一種自平衡樹狀資料結構,能對儲存的資料進行O(log n)的時間複雜度進行查詢、插入和刪除。B樹一般較多用在儲存系統上,比如資料庫或檔案系統。
B樹特點
- B樹可以定義一個m值作為預定範圍,即m路(階)B樹。
- 每個節點最多有m個孩子。
- 每個節點至少有ceil(m/2)個孩子,除了根節點和葉子節點外。
- 對於根節點,子樹個數範圍為[2,m],節點內值的個數範圍為[1,m-1]。
- 對於非根節點,節點內的值個數範圍為[ceil(m/2)-1,m-1]。
- 根節點(非葉子節點)至少有兩個孩子。
- 一個有k個孩子的非葉子節點包含k-1個值。
- 所有葉子節點在同一層。
- 節點內的值按照從小到大排列。
- 父節點的若干值作為分離值分成多個子樹,左子樹小於對應分離值,對應分離值小於右子樹。
以下是一個四階B樹,

插入
假設現在構建一棵四階B樹,開始插入“A”,直接作為根節點,

插入“B”,大於“A”,放右邊,

插入“C”,按順序排到最後,

繼續插入“D”,直接新增的結果如下圖,此時超過了節點可以存放容量,對於四階B樹每個節點最多存放3個值,此時需要執行分裂操作,

分裂操作為,先選取待分裂節點的中值,這裡為“B”,然後將中值“B”放到父節點中,因為這裡還沒有父節點,那麼直接建立一個新的父節點存放“B”,而原來小於“B”的那些值作為左子樹,原來大於“B”的那些值作為右子樹。

繼續插入“E”,"E"大於“B”,往右子節點,

分別於“C”和“D”比較,大於它們,放到最右邊,

插入“F”,“F”大於“B”,往右子樹,

“F”分別與“C”"D""E"比較,大於它們,放到最右邊,此時觸發分裂操作,

選取待分裂節點的中值“D”,然後將中值“D”放到父節點中,父節點中的“B”小於“D”,於是放到“B”右邊,而原來小於“D”的那些值作為左子樹,原來大於“D”的那些值作為右子樹。

繼續插入“M”,結果為,

插入“L’,大於“B”“D”,往右子樹,

“L”大於“E”“F”小於“M”,於是放到第三個位置,此時觸發分裂操作,

選取待分裂節點的中值“F”,然後將中值“F”放到父節點中,父節點中的“B”“D”都小於“F”,於是放到最右邊,而原來小於“F”的那些值作為左子樹,原來大於“F”的那些值作為右子樹。

插入“K”,結果為,

插入“J”,大於“B”“D”“F”,往右子樹,

“J”小於“K”“L”“M”,於是放到第一個位置,此時觸發分裂操作,

選取待分裂節點的中值“K”,然後將中值“K”放到父節點中,父節點中的“B”“D”“F”都小於“K”,於是放到最右邊,而原來小於“K”的那些值作為左子樹,原來大於“K”的那些值作為右子樹。此時父節點也觸發分裂操作,

選取待分裂節點的中值“D”,然後將中值“D”放到父節點中,由於還沒有父節點,那麼直接建立一個新的父節點存放“D”,而原來小於“D”的那些值作為左子樹,原來大於“D”的那些值作為右子樹。

插入“I”,大於“D”,往右子樹,

右子樹不是葉子節點,繼續往下,這時“I”大於“F”而小於“K”,所以往第二個分支,

“I”小於“J”,於是放到左邊,

類似地,插入“H”,結果如下,

插入“G”,往左子樹,

往中間分支,

觸發分裂操作,

選取待分裂節點的中值“H”,然後將中值“H”放到父節點中,"H"大於父節點中的“F”而小於“K”,於是放到中間,而原來小於“H”的那些值作為左子樹,原來大於“H”的那些值作為右子樹。

綜上所述,插入操作的核心是分裂操作。無需分裂的情況比較簡單,直接插入即可;如果插入後超過節點容量,這個容量可預先自定義,則需要進行分裂操作,需要注意的是分裂可能引起父節點需要繼續分裂。
查詢
對B樹進行查詢就比較簡單,查詢過程有點類似二叉搜尋樹,從根節點開始查詢,根據比較數值找到對應的分支,繼續往子樹上查詢。
比如查詢“I”,"I"大於“D”,往右子樹,

“I”分別與節點內值比較,大於“F”“H”而小於“K”,往第三個分支,

逐一比較節點內的值,找到“I”。

-------------推薦閱讀------------
我的開源專案彙總(機器&深度學習、NLP、網路IO、AIML、mysql協議、chatbot)
跟我交流,向我提問:

歡迎關注:
