樹的演化:從天真理想到硬體現實 (BST, Treap, B-Tree)

更新 發佈閱讀 7 分鐘

想像一下,你要在電腦中儲存上億筆資料,並且要能「快速」找到其中一筆。你腦中第一個閃過的念頭,可能就是像字典一樣的「二分搜尋法」。但是,字典(陣列)有個致命缺點:它很難「新增」或「刪除」資料。你如果在 B 和 C 之間加一個 B.5,後面的所有資料都要往後挪動,這在演算法上是 O(N) 的災難。

為了解決這個矛盾,天才的電腦科學家們發明了「樹 (Tree)」這種結構。它試圖同時擁有「陣列」的快速搜尋O(log N) 和「連結串列」的快速增刪O(1)。這三種樹,代表了這個夢想在不同層次上的實現與挑戰。

BST 是這個夢想的「原型機」,Treap 是它的「機率優化版」,而 B-Tree 則是為了適應「大型資料庫」這個殘酷戰場而徹底改造的「重裝機甲」。

BST (二元搜尋樹) — 夢想的起點與殘酷的現實

「二元搜尋樹 (Binary Search Tree)」是所有樹狀結構的共同祖先,它的發明背景就是為了結合「排序陣列」和「連結串列」的優點。它的運作原理極為單純優雅:對於樹中的任何一個節點,其「左子樹」上所有節點的值,都小於該節點的值;其「右子樹」上所有節點的值,都大於該節點的值。

這個簡單的規則,讓「搜尋」變得無比高效。當你要找一個數字 25 時,你從樹根(例如 50)開始,因為 25 < 50,你立刻排除了右半邊的所有可能,往左走;下一個節點是 20,25 > 20,你往右走... 每一步,你都能排除掉一半的資料,這就是 $O(\log N)$ 的魔力。同時,「新增」和「刪除」也變得容易,你只需要按照規則找到該放的位置,然後像 linked list 一樣把節點「掛」上去即可。

然而,BST 有一個阿基里斯腱,一個「致命缺陷」。它的 O(log N) 效能,完全依賴於樹的「平衡」。如果你的輸入資料「剛好是排好序的」(例如,你依次插入 1, 2, 3, 4, 5...),BST 就會退化成一條「歪斜」的長鏈,看起來就像一個 Linked List。此時,搜尋 5 就需要一步步走過 1, 2, 3, 4,效能從 O(log N) 驟降為 O(N)。這個「看運氣」的結構,在現實世界的嚴苛應用中是完全不可接受的。

Treap (Tree + Heap) — 用「機率」來對抗「宿命」

BST 的缺陷(對輸入順序敏感)引發了一場「平衡革命」。電腦科學家發明了各種複雜的「自平衡樹」,例如 AVL 樹或紅黑樹 (Red-Black Tree)。它們透過極其複雜的旋轉和顏色規則,來「嚴格保證」樹在任何插入或刪除後,都能維持平衡。但這些演算法的研發和實作都極為困難且容易出錯。

於是,在 1980 年代,Seidel 和 Aragon 提出了一個絕妙的「機率性」解決方案,這就是 Treap。它的名字是 Tree 和 Heap (堆積) 的結合體。它的研發背景就是:「我們能不能不要用那麼複雜的確定性規則,而是用『隨機』的力量,讓樹『統計上』保持平衡?」Treap 的運作原理是,它給每個節點賦予兩個屬性:一個是它的「鍵值 (Key)」(和 BST 一樣,用來排序),另一個則是「隨機產生的優先級 (Priority)」。

Treap 必須同時滿足兩個條件:1. 從「鍵值」來看,它是一棵二元搜尋樹(左 < 中 < 右)。 2. 從「優先級」來看,它是一棵堆積 (Heap)(父節點的優先級 > 子節點的優先級)。當你插入一個新節點時,你先按 BST 規則找到位置,然後賦予它一個隨機優先級。如果這個子節點的優先級高於父節點(違反了 Heap 規則),就執行「旋轉 (Rotation)」操作,把它「旋轉」上去,直到它滿足 Heap 規則為止。由於優先級是完全隨機的,你「剛好」插入 1, 2, 3... 這種有序資料,也會因為隨機優先級的「攪和」,而自動形成一棵大致平衡的樹。Treap 用「機率」優雅地破解了 BST 的「宿命」。

B-Tree — 專為「硬碟」打造的「矮胖」王者

如果說 Treap 是為了修正 BST 的「演算法缺陷」O(N),那麼 B-Tree 就是為了解決一個截然不同的「硬體問題」。這個問題就是我們在上一篇文章談到的「快取 (Cache)」,但規模還要大上百萬倍:RAM (記憶體) 與 Disk (硬碟) 之間的天懸地隔。CPU 存取 RAM 可能 100 奈秒,但存取硬碟(Disk I/O)可能需要 10 毫秒(10,000,000 奈秒),這之間有著 10 萬倍的速度差。

B-Tree 的研發背景是大型資料庫和檔案系統。如果我們把一個有上億筆資料的 BST 或 Treap 存放在硬碟上,一次 O(log N) 的搜尋(假設 log N = 30)將意味著 30 次「隨機的硬碟讀取」。這會讓使用者等到天荒地老。B-Tree 的設計哲學就是:「既然硬碟 I/O 如此昂貴,我們就必須不惜一切代價,將 I/O 的次數降到最低!」它的運作原理,是徹底拋棄「二元」的限制,打造一棵極度「矮胖」的樹。

B-Tree 的一個「節點」不再只存 1 個鍵值,而是被設計得非常巨大(例如 4KB,剛好對應硬碟一個「區塊 (Block)」的大小),使其內部可以存放數百甚至數千個鍵值。這使得樹的「階度 (Order)」非常高,不再是 2,而是 M (例如 M=1000)。因此,樹的高度變得極低,從 $O(log_2 N) 變成了 O(log_M N)。一棵儲存了 10 億筆資料的 B-Tree,其高度可能只有 3 或 4 層!當你搜尋時,你付出第一次昂貴的 I/O 把「根節點」(含 1000 個鍵值)載入 RAM,然後在 RAM 中用極快的二分搜尋法找到「下一層」的指標,再付出第二次 I/O... 僅僅 3-4 次硬碟存取,你就找到了資料。B-Tree 是為「最小化 I/O」而生的工程奇蹟。

為不同戰場而生的「樹」

這三種樹,完美地展現了電腦科學的演化:

  • BST (二元搜尋樹):是「理論原型」。它提出了 O(log N) 的美好理想,但卻在「最壞情況」下(有序輸入)徹底失敗。
  • Treap:是「演算法的優化」。它使用「機率」作為武器,用一種優雅且實作相對簡單的方式,「統計上」解決了 BST 的平衡問題,使其效能穩定在 O(log N)。
  • B-Tree:是「硬體工程的結晶」。它根本不在乎單一節點的平衡,它在乎的是「I/O 次數」。它透過「矮胖」的結構,犧牲了 RAM 內部的大量比較,換取了最少的硬碟讀取次數,是所有現代資料庫的基石。

BST 是夢想的起點,Treap 是對演算法的精煉,而 B-Tree 則是對硬體現實的最高致敬。


留言
avatar-img
留言分享你的想法!
avatar-img
政大雜學筆記
1會員
37內容數
我在政大的一些紀錄
政大雜學筆記的其他內容
2025/11/05
本文深入探討了演算法分析中「平均情況分析」與「攤銷分析」的根本區別。文章以動態陣列和快速排序為例,生動地闡述了前者基於機率,提供的是對隨機輸入的期望值,允許極端情況的存在;後者則基於操作序列,直面最壞情況,提供的是對整體效能的保證。
2025/11/05
本文深入探討了演算法分析中「平均情況分析」與「攤銷分析」的根本區別。文章以動態陣列和快速排序為例,生動地闡述了前者基於機率,提供的是對隨機輸入的期望值,允許極端情況的存在;後者則基於操作序列,直面最壞情況,提供的是對整體效能的保證。
2025/11/05
探討為何在相同的 Big O 時間複雜度下,Array 的執行速度遠超 Linked List。文章深入剖析 CPU Cache 的運作原理,包括時間局部性與空間局部性,以及快取命中未命中的影響,解釋 Array 如何連續記憶體配置優化快取效能,而 Linked List 則因指標跳躍導致效能低落。
2025/11/05
探討為何在相同的 Big O 時間複雜度下,Array 的執行速度遠超 Linked List。文章深入剖析 CPU Cache 的運作原理,包括時間局部性與空間局部性,以及快取命中未命中的影響,解釋 Array 如何連續記憶體配置優化快取效能,而 Linked List 則因指標跳躍導致效能低落。
2025/11/05
為何 Google 搜尋秒搜全球,選課系統卻瞬間崩潰?關鍵在於「演算法」的策略!本文帶你深入淺出理解時間複雜度,認識 Big O(上界)、Big Omega(下界)與 Big Theta(緊束界),洞悉程式快慢的本質,讓你不再被程式設計的「祕密語言」所難倒。學會評估演算法效率,做出最佳策略選擇。
2025/11/05
為何 Google 搜尋秒搜全球,選課系統卻瞬間崩潰?關鍵在於「演算法」的策略!本文帶你深入淺出理解時間複雜度,認識 Big O(上界)、Big Omega(下界)與 Big Theta(緊束界),洞悉程式快慢的本質,讓你不再被程式設計的「祕密語言」所難倒。學會評估演算法效率,做出最佳策略選擇。
看更多
你可能也想看
Thumbnail
期末考結束了!這學期我負責開了大二必修課:資料結構(Data Structure),雖然作業、小考都是由TA(課程助理)幫我批改,不過期中期末考都是我自己把所有的考卷改完的。總共大約是將近150份的考卷,有兩個班。而期末考的內容,其實基本上我把大部分的規則、程式碼都寫在題目裡面或者放在附錄讓同學可以
Thumbnail
期末考結束了!這學期我負責開了大二必修課:資料結構(Data Structure),雖然作業、小考都是由TA(課程助理)幫我批改,不過期中期末考都是我自己把所有的考卷改完的。總共大約是將近150份的考卷,有兩個班。而期末考的內容,其實基本上我把大部分的規則、程式碼都寫在題目裡面或者放在附錄讓同學可以
Thumbnail
請使用 C 或 Java 語言寫一副程式,此副程式對一個長度為 10 的整數陣列 A[0:9],最多花費 15 次的數值比較運算,尋找陣列中的最小值及最大值,並分別存入 Min 及 Max。(注意:請加註解說明程式碼寫法) 【103.關務】
Thumbnail
請使用 C 或 Java 語言寫一副程式,此副程式對一個長度為 10 的整數陣列 A[0:9],最多花費 15 次的數值比較運算,尋找陣列中的最小值及最大值,並分別存入 Min 及 Max。(注意:請加註解說明程式碼寫法) 【103.關務】
Thumbnail
這篇文章說明演算法的定義、特徵以及一個有趣的小知識。演算法被定義為解決問題的流程,並以機車故障為例說明。文章也列出演算法的五個特徵:輸入、有限性、明確性、有效性及輸出。最後,文章提及世界上公認的第一個演算法是歐幾裡德演算法。
Thumbnail
這篇文章說明演算法的定義、特徵以及一個有趣的小知識。演算法被定義為解決問題的流程,並以機車故障為例說明。文章也列出演算法的五個特徵:輸入、有限性、明確性、有效性及輸出。最後,文章提及世界上公認的第一個演算法是歐幾裡德演算法。
Thumbnail
上次我們提到了演算法(algorithm),它是一種解決問題的方式。但演算法只是資料結構與演算法(Data Structures and Algorithms, DSA)這個領域的一部分。今天,我們要進一步探索這個主題,了解它的核心概念。 什麼是資料結構與演算法呢?簡單來說,資料結構是用來組織和存
Thumbnail
上次我們提到了演算法(algorithm),它是一種解決問題的方式。但演算法只是資料結構與演算法(Data Structures and Algorithms, DSA)這個領域的一部分。今天,我們要進一步探索這個主題,了解它的核心概念。 什麼是資料結構與演算法呢?簡單來說,資料結構是用來組織和存
Thumbnail
在資料結構與演算法裡, 最簡單的線性資料結構除了array之外就是linked list鏈結串列了。 Linked list又有分為單向Singly linked list 和雙向Doubly linked list
Thumbnail
在資料結構與演算法裡, 最簡單的線性資料結構除了array之外就是linked list鏈結串列了。 Linked list又有分為單向Singly linked list 和雙向Doubly linked list
Thumbnail
從範例學python的目標讀者: 針對剛進入的初學者,想學習Python語言。 有基礎本數學邏輯基礎即可。 從小遊戲學python的目標讀者: 針對已經有經驗的C/C++, Python, 或其他有程式基礎的讀者。 想實作一些小專案,從實做中學習如何分析需求、元件分拆、到底層實作
Thumbnail
從範例學python的目標讀者: 針對剛進入的初學者,想學習Python語言。 有基礎本數學邏輯基礎即可。 從小遊戲學python的目標讀者: 針對已經有經驗的C/C++, Python, 或其他有程式基礎的讀者。 想實作一些小專案,從實做中學習如何分析需求、元件分拆、到底層實作
Thumbnail
在資料結構與演算法裡, 最簡單的線性資料結構除了list之外就是linked list鏈結串列了。 Linked list又有分為單向Singly linked list 和雙向Doubly linked list 在這篇文章,會從最基礎的Singly linked list開始講起。 定義
Thumbnail
在資料結構與演算法裡, 最簡單的線性資料結構除了list之外就是linked list鏈結串列了。 Linked list又有分為單向Singly linked list 和雙向Doubly linked list 在這篇文章,會從最基礎的Singly linked list開始講起。 定義
Thumbnail
本文將深入探討鏈表的核心概念,使用 JavaScript 來說明如何實現和操作鏈表(Linked List),包括 append、prepend、remove、find 和 reverse 等五大方法。
Thumbnail
本文將深入探討鏈表的核心概念,使用 JavaScript 來說明如何實現和操作鏈表(Linked List),包括 append、prepend、remove、find 和 reverse 等五大方法。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News