CS61B資料結構及演算法筆記:(十二)平衡樹(B-Tree)

Evan
Nov 26, 2023

--

依上一個章節介紹,改變對 BST 插入節點的順序會影響最終的結構,導致搜尋節點的時間複雜度最差的情況為 O(N),最佳為 O(logN)。而時間複雜度又和搜尋節點的次數有關,一顆 BST 所有的節點中,依二元搜尋法需要經過最多次的節點為 leaf,因此先定義樹的高度為 leaf 和 root 的距離。考慮最差的情況,樹的高度與節點數量成線性成長,搜尋 leaf 所需要經過的節點最多,耗費時間最長;另一方面,假設 BST 的高度增加前,能先把同一個高度存放節點的位置填滿,搜尋的時間複雜度則會是最佳的 O(logN),如圖一所示。

圖一

此時思考一個大膽的想法,既然時間複雜度和樹的高度相關,是否存在一種的方法,可以在插入節點的同時,又可以不增加樹的高度。首先,將每個節點視為可以容納 L 個節點的"複合節點"(e.g. 列表),需要新增節點時,一律從 leaf 插入節點,如圖二所示。此時這棵樹只會增加 leaf 容納節點的數量,永遠不會增加樹的高度。顯然地,這個方法雖然不會增加樹的高度,不過搜尋過程所經過的節點也會隨著 L 增加而增加,搜尋的時間複雜度依舊是 O(N)。

圖二

如果不希望 L 無止盡的擴張,則限制 L 的大小為一常數,假設對 leaf 插入節點後的節點數量大於 3,也就是含 4 個節點時,將第二個(四個節點中第二大的節點)節點推向父節點。如在圖三中,節點 8 因節點 10 的插入而被推向父節點,與節點 6 形成複合節點。

圖三

父節點增加從子節點被擠出來的節點後,成為具有兩個節點的複合節點,此時父節點不再只有 BST 中二元的特性(left 成員接小於父節點;right 成員皆大於父節點),而是新增了第三個成員 middle,依據子節點的值

  1. 皆小於父節點包含的兩個節點
  2. 皆大於父節點包含的兩個節點
  3. 在父節點包含的兩個節點之間

重新排列,進而分裂出新的子節點 7,如圖四所示。

圖四

按照這個規則繼續將節點一一插入 leaf,從圖五可以發現整棵樹的高度會因為節點持續被往上推的關係,像是從往下增加變成往上增加。且當 leaf 增加節點後的數量超過 3 個的時候,會在與其他 leaf 同一個高度的位置,分裂出新的 leaf,換句話說,所有 leaf 的高度永遠相同。除此之外,每個非 leaf 節點會維持,子節點數量等於父節點包含節點數量 + 1 的特性。

圖五
圖六

也因為所有 leaf 高度皆相同,這棵永遠維持平衡的樹又稱 2–3–4 樹,2–3–4 代表每個複合節點根據自己包含節點的數量,可能有 2、3、4 個子節點。當 L 的長度等於 2 時,插入節點的規則和 2-3-4 樹一樣,又稱 2–3 樹,之後都會以 2-3 樹為預設平衡樹介紹。不管是哪一種平衡樹,都維持上述性質。

介紹 BST 時談到插入節點的順序會影響樹的結構,平衡樹也是一樣,不同的插入順序也會影響樹的高度,如圖七所示,可以發現當每個複合節點包含的節點數量愈多,樹的高度則愈低。

圖七

另一方面,在節點數量 L 的限制之下,搜尋每個複合節點時,如果該節點包含最多的節點,則每個高度都需要搜尋 L 個節點。因此整個搜尋的時間複雜度須將高度的複雜度乘上長度的複雜度 O(LH) 而長度又為常數,只要考慮高度的複雜度 O(H) 即可,因此時間複雜度為 O(logN)。

刪除節點的步驟則比新增節點來得更複雜,不在這裡敘述。即使如此,雖然對平衡樹插入任意順序的節點都能隨時保持平衡,不過實際上要完成平衡樹插入的代碼,需要不斷追蹤每個節點且分裂,相當複雜,接下來會介紹如何維持平衡樹的優點,以更簡單的方式完成平衡樹。

--

--