跳转至

2-3-4 樹

本頁介紹 2-3-4 樹。在計算機科學中,2–3–4 樹(也稱為 2–4 樹)是一種自平衡的樹,所有的葉子節點都位於同一深度。2-3-4 樹是 4 階 B 樹,與一般的 B 樹一樣,2-3-4 樹可以實現在 \(O(\log n)\) 時間內進行搜索、插入和刪除操作。

2-3-4 樹是對 2-3 樹的概念擴展,包括了 4 節點的使用。

2–3–4 樹與紅黑樹是等價的。對於每個 2–3–4 樹,恰好存在一個相同順序的紅黑樹。此外,對 2–3–4 樹進行插入和刪除操作,導致節點擴展、分裂和合並的過程與紅黑樹中的顏色翻轉和旋轉等效。2–3–4 樹的操作中涉及大量特殊情況,在大多數編程語言中的實現可能會比較困難。而紅黑樹的實現較為簡單,因此更常被使用。

定義

2-3-4 樹的節點分為三種,2 節點,3 節點和 4 節點,分別包含一個、兩個或三個數據元素。

2-3-4-tree-2-node

2-3-4-tree-3-node

2-3-4-tree-4-node

性質

  • 每個節點(葉節點或內部節點)可以是 2 節點、3 節點或 4 節點,分別包含一個、兩個或三個數據元素。
  • 所有的葉子節點都處於同一深度(最底層)。
  • 所有數據都有序存儲。
  • 在 2-3-4 樹的最壞情況下高度是 \(\log N\),在最好的情況下高度是 \(1/2 \log N\)(所有節點都是 4 節點)。

操作

由於 2-3-4 樹上的元素是按一定順序存儲的,所以查找與二叉搜索樹類似。插入和刪除都會保持平衡性,即葉節點的深度相同。

插入

2-3-4 樹的插入只會發生在葉節點,而不會發生在內部節點。在葉節點上插入一個值,會讓 2 節點變成 3 節點,3 節點變成 4 節點,而 4 節點就沒有空間了。為了解決這個問題,這裏有兩種思路。

  • 自底向上,從 4 葉節點開始分裂(上溢),如果分裂後其父節點也是 4 節點,繼續向上分裂,直到到達根節點。如果根節點也是 4 節點,分裂後樹的高度 + 1。
  • 自頂向下,從根節點到插入所在的葉節點路徑上,遇到 4 節點就將其分裂。

本文采用自底向上,將插入操作分為兩部分,首先根據搜索樹的性質在葉節點的位置插入,形成暫時的 5 節點,然後根據插入節點及相關節點的狀態進行平衡的維護。

下面是 2-3-4 樹插入的例子。

首先插入值 \(25\)。從根節點 \((10,20)\) 開始查找,找到包括 \(25\) 的區間 \((20,\infty)\),進入右子節點 \((22,24,29)\)

2-3-4-tree-insert-1

由於節點 \((22, 24, 29)\) 是一個 4 節點,先將 \(25\) 插入形成暫時的 5 節點

2-3-4-tree-insert-2

由於 \((22, 24, 25, 29)\) 超出了 4 節點,需要將第二個元素上溢。即將 \(24\) 與父節點合併形成 \((10,20,24)\)

2-3-4-tree-insert-3

然後插入 \(30\)

2-3-4-tree-insert-4

插入 \(31\)。形成 5 節點 \((25,29,30,31)\)

2-3-4-tree-insert-5

第二個元素 \(29\) 上溢,發現父節點也形成 5 節點 \((10,20,24,29)\)

2-3-4-tree-insert-6

5 節點 \((10,20,24,29)\) 中第二個元素 \(20\) 繼續上溢,最終調整完成。

2-3-4-tree-insert-7

刪除

從 2-3-4 樹中刪除一個元素需要根據待刪除元素的位置分情況討論。刪除操作只發生在葉節點上。下面介紹從 2–3–4 樹中刪除一個元素的過程:

  1. 查找需要刪除某個元素的節點。
  2. 如果節點是葉子節點(3 節點或 4 節點),則從該節點中刪除所需的值,並將數據元素減少 1。
  3. 如果節點不是葉子節點,則:
    • 查找該節點的後繼節點。節點的後繼是其中大於它的最小元素或小於它的最大元素。
    • 用後繼節點交換當前節點,並在葉子中刪除該節點。

但如果葉節點是一個 2 節點,刪除該節點可能會導致下溢(underflow)。為了避免這種情況,我們在從上到下移動到要刪除的節點的路徑上遇到 2 節點時,會先執行以下調整操作,使找到的葉子節點不是 2 節點。這樣,在之後的交換和刪除後,不會出現一個空的葉節點。

Case 1

如果當前節點的兄弟節點之一是 3 節點或 4 節點,則將當前節點與那個兄弟節點一起進行以下旋轉操作:

  1. 選擇一個與當前節點最接近的鍵值的兄弟節點。
  2. 將該兄弟節點的鍵值上移到當前節點的父節點中,使父節點變成一個 3 節點。
  3. 原來兄弟節點的孩子現在成為當前節點的孩子。

下圖在 2-3-4 樹中刪除元素 \(10\)

2-3-4-tree-delete-1

Case 2

如果父節點是 2 節點並且兄弟節點也是 2 節點。在這種情況下,父節點是根節點。所以將這三個 2 節點合併成一個新的 4 節點,並縮短樹的高度。

下圖在 2-3-4 樹中刪除元素 \(50\)

2-3-4-tree-delete-2

Case 3

如果兄弟節點是 2 節點,但父節點是 3 節點或 4 節點:

  1. 將相鄰的兄弟節點和俯視兩個兄弟節點的父鍵融合成一個 4 節點(下溢)。
  2. 將兄弟節點的子節點移到該節點。

下圖在 2-3-4 樹中刪除元素 \(50\)

2-3-4-tree-delete-3

與紅黑樹的關係

2-3-4 樹和紅黑樹是同構的,任意一棵紅黑樹都唯一對應一棵 2-3-4 樹。在 2-3-4 樹上的插入和刪除操作導致節點的擴展、分裂和合並,相當於紅黑樹中的變色和旋轉。下圖是 2-3-4 樹的 2 節點、3 節點和 4 節點對應的紅黑樹節點。注意到 2-3-4 樹的 3 節點對應紅黑樹中紅色節點左偏和右偏兩種情況,所以一棵紅黑樹可能對應多棵 2-3-4 樹。

2-3-4-tree-rbt-1

下圖是一棵紅黑樹和與之對應的 2-3-4 樹。將紅黑樹中的紅色節點上移到父節點的左右兩側,形成一個 B 樹節點,就可以得到與之對應的 2-3-4 樹。可以發現,紅黑樹的節點數等於 2-3-4 樹的節點個數。

2-3-4-tree-rbt

下面用上圖對比紅黑樹和 2-3-4 樹的插入和刪除操作。

紅黑樹的插入

下面根據插入節點,父節點以及祖父節點的情況將插入分為以下 3 類情況,每類情況均包含 4 種。

  • 插入節點的父節點是黑色,可以直接插入,無需調整。
  • 插入節點的叔父節點但不為紅色(不存在或者為黑色),需要進行變色 + 旋轉(單旋或雙旋)。
  • 插入節點的叔父節點是紅色,需要進行變色並將祖父節點上溢,遞歸處理。

Case 1

插入節點的父節點是黑色,可以直接插入,不需要進行調整。這種情況插入新節點後形成 2-3-4 樹的 3 節點或 4 節點。如下圖插入 \(9、22、28、40\) 都屬於這種情況。

2-3-4-tree-rbt

Case 2

插入節點的父節點是紅色,祖父節點是黑色,叔父節點不為紅色(不存在或者為黑色)。這種情況插入後形成 2-3-4 樹的 4 節點,但是違反紅黑樹的性質(出現兩個連續紅色節點)。按照父節點和祖父節點的位置可以分成 LL、RR、LR、RL 四種情況。其中 LL 和 RR 需要進行一次染色和單旋(左旋或右旋)。LR 和 RL 則需要進行染色和雙旋。

如下圖插入節點 \(11、13、16、19\) 都屬於這種情況。其中插入節點 \(13\)\(16\) 為 RR 型和 LL 型,\(11\)\(19\) 為 RL 型和 LR 型。

2-3-4-tree-rbt

以插入節點 \(13\) 舉例,按照 2-3-4 樹的插入方式中應將 \(13\) 插入 \(12\) 的右側,但出現兩個連續紅色節點 \(12\)\(13\),因此需要先將父節點 \(12\) 染成黑色,將祖父節點 \(10\) 染成紅色並左旋祖父節點 \(10\),調整後滿足紅黑樹的性質。調整後的結果如下圖所示。

2-3-4-tree-rbt

Case 3

插入節點的父節點和叔父節點是紅色,祖父節點是黑色。這種情況其實是形成了一個 2-3-4 樹的 5 節點。需要將父節點和叔父節點染為黑色,將祖父節點染為紅色並上溢到父節點,然後遞歸處理。如下圖插入節點 1、3、5、7 都屬於這種情況。

2-3-4-tree-rbt

下圖以插入節點 \(1\) 為例,形成的 5 節點為 \((1,2,4,6)\)。將父節點 \(2\) 和叔父節點 \(6\) 染為黑色,將祖父節點 \(4\) 染為紅色並上溢。可以發現節點 \(4\) 上溢後仍需要按照 Case 3 處理,此處省略具體步驟。其他三種情況的調整方式與之類似。

2-3-4-tree-rbt

紅黑樹的刪除

紅黑樹內部節點的刪除,會轉換為其前驅或後繼節點的刪除。所以紅黑樹節點的刪除,最終都會轉換為 2-3-4 樹中最後一行節點的刪除(對應紅黑樹最後兩行節點的刪除)。

Case 1

待刪除節點為紅色,可以直接刪除,因為刪除最後一層的紅色節點不會違反紅黑樹的性質。

Case 2

待刪除的節點為黑色,需要進行調整才能滿足紅黑樹的黑平衡。其中黑色節點可以分為以下三種情況。

Case 2.1

待刪除的黑色節點有兩個紅色子節點的黑色節點(2-3-4 樹的 4 節點)。刪除該節點會轉換為其前驅或後繼節點的刪除,所以此處不做考慮。

Case 2.2

待刪除的黑色節點有一個紅色子節點的黑色節點(2-3-4 樹的 3 節點)。需要用唯一的紅色子節點替代被刪除的節點,並將該紅色子節點染為黑色。如下圖刪除節點 \(10\),需要用唯一的紅色子節點 \(12\) 替代,並將其染為黑色。

2-3-4-tree-rbt

調整完的結果如下圖所示。

2-3-4-tree-rbt

Case 2.3

待刪除的黑色節點為黑色葉節點(對應 2-3-4 樹刪除葉子 2 節點導致下溢的情況),需要進行調整,具體可以分為以下三種情況。

Case 2.3.1

待刪除節點為根節點,當前只有一個節點,可以直接刪除。

Case 2.3.2

待刪除節點的兄弟節點為黑色。

  • 兄弟節點有紅色子節點(分為三種,有一個左紅色子節點,或有一個右紅色子節點,或兩個紅色子節點),需要借用兄弟節點的子節點進行調整。下圖以刪除節點 \(36\),其有一個紅色的左子節點 \(18\) 為例,這種情況需要進行旋轉和染色。觀察到待刪除節點的父節點、兄弟節點、兄弟節點的子節點的關係為 LL 型,只需要右旋一次,並將中間節點 \(20\) 染為紅色,兩個子節點 \(18\)\(25\) 染為黑色。

    2-3-4-tree-rbt

    下圖是調整後的結果。其他兩種情況,有一個紅色的右子節點的情況需要進行雙旋和染色,有兩個紅色的子節點的情況可以任選其中一個進行調整。

    2-3-4-tree-rbt

  • 兄弟節點沒有紅色子節點,需要將兄弟節點染紅,父節點染黑。如果父節點原來就為黑色,需要把父節點當作已被刪除的節點處理,然後向上遞歸調整。

    如下圖所示,待刪除節點 \(36\),其兄弟節點 \(20\) 沒有紅色子節點,父節點 \(25\) 為紅色,需要將父節點下溢(對應 2-3-4 樹中刪除葉子 2 節點將父節點下溢的情況,在紅黑樹中無需操作)並染黑,將 \(20\) 染紅。

    2-3-4-tree-rbt

    下圖是待刪除節點的父節點原來就為黑色的情況。調整後由於父節點 \(25\) 發生了下溢,需要把父節點 \(25\) 當作一個刪除的節點向上遞歸調整。

    2-3-4-tree-rbt

Case 2.3.3

待刪除節點的兄弟節點為紅色,需要轉變為兄弟節點為黑色的情況進行處理。具體操作是將父節點旋轉,將兄弟節點染黑,父節點染紅,然後轉換成待刪除節點的兄弟節點為黑色的情況繼續進行調整。如下圖所示,待刪除待刪除節點 \(36\),其兄弟節點 \(15\) 為紅色,根據位置關係可知需要將父節點右旋,並將父節點染紅,兄弟節點染黑,轉換成待刪除節點的兄弟節點為黑色的情況。

2-3-4-tree-rbt

參考資料

  1. 2–3–4 tree - Wikipedia
  2. 2-3-4 Trees | Algorithm Tutor
  3. 2-3-4 Tree - GeeksforGeeks