1. 程式人生 > 其它 >資料結構與演算法(十二)

資料結構與演算法(十二)

多路查詢樹

二叉樹與 B 樹

二叉樹存在的問題

二叉樹的操作效率較高,但是也存在問題,如下圖所示

當二叉樹的節點較少時,不會出現什麼問題。但是當節點過多時(海量,如 1 億),就會出現如下的問題:

  1. 構建二叉樹時,需要進行多次 I/O 操作

    節點較多時,一般會儲存在檔案或則資料庫中,進行多次 I/O 獲取到所有的節點,速度有影響

  2. 會造成二叉樹的高度很大,降低操作速度

多叉樹

為了解決層數過多的問題,就出現了 多叉樹

在二叉樹中,每個節點有資料項,最多有兩個子節點。如果允許每個節點可以有更多的資料項和更多的子節點,就是 多叉樹(multiway tree)

多叉樹也有一定的規則的,比如後面講解的 2-3 樹、2-3-4 樹,就是多叉樹。

多叉樹通過重新組織節點,減少樹的高度,對二叉樹進行優化。

下圖則是一顆 2-3 的多叉樹:

2-3 的原因:如上圖按節點數量來定義的。有的只有 2 個子節點,有的有 3 個子節點。

B 樹的基本介紹

B 樹通過重新組織節點,降低樹的高度,並且減少 I/O 讀寫次數來提升效率。

上圖說明:

  • 一個圓圈表示一個數據項
  • 相連的資料項,整個表示一個節點

那麼它的有點如何理解呢?

  • 降低樹的高度:

    可以看到,一個節點中有很多資料項,就能大大減少節點數量,從而降低樹的高度

  • 減少 I/O 讀寫次數

    檔案系統及資料庫系統的設計者利用了 磁碟預讀原理將一個節點的大小設為等於一個頁(通常大小為 4K)

    ,這樣每個節點只需要一次 I/O 就可以完全載入。

    這樣說,你可能沒有概念,舉個例子:將樹的度 M 設定為 1024 ,在 600 億個元素中最多隻需要 4 次 I/O 操作就可以讀取到想要的元素。

    B 樹(B+ )廣泛應用於檔案儲存系統以及資料庫系統中。

    什麼是

    • 節點的度:一個節點下的子樹節點個數就是 節點的度。
    • 樹的度:指一顆樹中,節點的度最大的哪一個值。

B 樹其實就是前面所說的 多叉樹

2-3 樹

2-3 樹是最簡單的 B 樹結構,具有如下特點:

  1. 所有 葉子節點 都在同一層

    只要是 B 樹都滿足這個條件,就是滿樹。

  2. 有兩個子節點的節點叫 二節點

    二節點要麼 沒有子節點,要麼 必須有兩個子節點

  3. 有三個子節點的節點叫 三節點

    三節點要麼 沒有子節點,要麼 必須有三個子節點

  4. 2-3 樹是由 二節點三節點 構成的樹

2-3 樹構建圖解

對數列 {16, 24, 12, 32, 14, 26, 34, 10, 8, 28, 38, 20} 構建成一個 2-3 樹,那麼它構建的規則要滿足前面說的特點。下面進行圖解後,你就明白,上面的特點是如何限制的。

有幾個額外的注意事項:

  1. 一個節點中,最多隻允許放 2 個數據
  2. 構建的樹必須是有序的,也就是按照二叉排序(BST)的要求構建有序的樹

下面是圖解步驟:

  1. 新增 16、24

新增 16 時,沒有資料,直接新建一個節點,放進去。

新增 24 時,發現有一個節點了,並且比 16 大,此時該節點中只有一個數據,則將 24 放在 16 的右邊。

  1. 新增 12

此時會發現,12 比 16 小,本來應該放在 16 的左邊,此時發現這個節點 已經有兩個資料了,那麼就只能放在 左子節點

如果直接將 12 放到 16,24 的左節點,就會破壞 2-3 樹的條件:2 節點,要麼沒有子節點,要麼有兩個

那麼此時就只能將 16,24 這個節點進行拆分。如上圖:24 變成 16 的右節點,12 變成 16 的左節點。這時就滿足了 2-3 的特性。

  1. 新增 32

    這個就簡單了,以現在的樹結構,可以直接新增到 24 的 右邊,變成 24,32

  2. 新增 14

    這個也簡單,直接新增到 12 的右邊,變成 12,14

  3. 新增 26

    此時應該新增到 24,32 的中間,由於一個節點只能新增兩個資料,那麼就需要拆分。

為了滿足 B 樹特點,發現上層的 16 只有一個數,那麼就補足它。組成 16,26

因為此時 24,32 這個節點,不滿足 BST 的排序了,24 是小於 26 的。只有 32 滿足。

拆完上層,再拆本層:由於 24 介於 16,26 之間,則將它安排在 3 節點中的中間節點24,32 把 24 拆分出去了,只剩下 32,此時完全滿足 B 樹的特點。

  1. 新增 34

    此時就簡單了,新增到 32,34

  2. 新增 10

    此時應該新增到 12,14 的左側。但是不滿足條件:一個節點最多隻能裝 2 個數據。

    放到 12,14 的左節點,也不滿足條件:所有葉子節點必須在同一層、也不滿足 2-3 節點的數量要求。

    那麼此時就需要拆分,先看他的上層 16,26 是滿的,如何做呢?看下圖:

左側的拆分圖,上面我們分析過了,不滿足 B 樹要求。那麼就需要拆分成右圖這樣:

  1. 12,14 中的 14 拆分成 右子節點,10 掛在 左節點。

  2. 此時不滿足 B 樹要求的,則將 16,26 中的 26 拆分成 右子節點。

  3. 24 這個節點由於上層被拆分了,不滿足在中間節點了。調整它的位置

  4. 原來的 32,34 節點調整為 16 的右節點。

  5. 新增 8

    此時很簡單,組成 8,10 即可

  6. 新增 28

  7. 新增 38

    此時就簡單,直接組成 34,38

  8. 新增 20

    這個也簡單,直接組成 20,24

2-3 樹新增規則總結

滿足如下特點:

  1. 所有 葉子節點 都在同一層

    只要是 B 樹都滿足這個條件,就是滿樹。

  2. 有兩個子節點的節點叫 二節點

    二節點要麼 沒有子節點,要麼 必須有兩個子節點

  3. 有三個子節點的節點叫 三節點

    三節點要麼 沒有子節點,要麼 必須有三個子節點

  4. 2-3 樹是由 二節點三節點 構成的樹

  5. 構建的樹,要滿足二叉排序樹(BST) 的順序

  6. 一個節點中,最多隻允許放 2 個數據

  7. 只有一個數據的節點,下面只允許 最多有 2 個節點,要麼沒有

  8. 有 2 個數據的節點,下面只允許 最多有 3 個節點,要麼沒有

234 樹

除了 2-3 樹,還有 2-3-4 樹,他的特點是在 2-3 樹的基礎上,還多了一個 4 節點,同樣,一個節點最多可以裝 3 個數據,要麼有 4 個節點,要麼沒有

B樹、B+樹、B*樹

B樹

B-tree 樹即 B 樹,B 是 Balanced 平衡的意思。B- 樹,這個也是 B 樹,只是翻譯的文字容易產生誤解。

上圖就是一個 B 樹,說明如下:

  • B 樹的階:節點的最多 子節點 個數

    如:2-3 樹的階是 3,2-3-4 樹的階是 4

  • B 樹的搜尋

    根節點開始,對節點內的關鍵字(有序)序列進行二分查詢,如果命中則結束,否則進入查詢關鍵字所屬範圍的 兒子節點

    然後重複,直到所對應的兒子節點指標為空,或則已經是葉子節點。

  • 關鍵字集合分佈在整棵樹中

    即:葉子節點和非葉子節點都存放資料

  • 搜尋有可能在非葉子節點結束

  • 其搜尋效能等價於在關鍵字全集內做一次二分查詢

B + 樹

在 MySQL 中,有些索引就是用 B 樹或則 B+ 樹實現的。B+ 樹是 B 樹的變體,也是一種多路搜尋樹。

B + 樹說明:

  1. B+ 樹的搜尋與 B 樹基本相同,區別是 B+ 樹只有到達葉子節點才命中(B 樹可以在非葉子節點命中),其效能也等價於在關鍵字全集做一次二分查詢

  2. 所有 關鍵字都出現在葉子節點的連結串列中

    即:資料只能在葉子節點,也叫 稠密索引,且連結串列中的關鍵字(資料)恰好是有序的。

  3. 不可能在非葉子節點命中

  4. 非葉子節點相當於是葉子節點的索引,也叫 稀疏索引,葉子節點相當於是儲存(關鍵字)資料的資料層

  5. 更適合檔案索引系統

  6. B 樹和 B+ 樹有各自的應用場景,不能說 B+ 樹完全比 B 樹好。

B+ 樹的這種設計,應該是類似分段思想,比如:5,28,65,下面存放三個節點:

  • 5-28 的段,為一個節點
  • 28-65 的段,為一個節點
  • 65 以上的段,為一個節點

比如查詢 30 ,直接找到在第二個節點中,然後往下一個目錄索引找,就很快能定位到資料。

B*

B* 樹是 B+ 樹的變體,在 B+ 樹的非根和非葉子節點再增加指向兄弟的指標

說明:

  • B* 樹定義了 非葉子節點 關鍵字個數至少為 (2/3)*M ,即塊的最低使用率為 2/3,而 B+ 樹的塊的最低使用率為 B+ 樹的 1/2

    M 是指樹的度,也就是層。

  • 從第 1 個特點,可以看出 B* 樹分配新節點的概率比 B+ 樹要低,空間使用率更高。