知識的雙軌:邏輯之樹與記憶體迷宮 (1960-1970)

更新 發佈閱讀 16 分鐘

如果說 1950 年代是電腦的「史前時代」,那麼 1960 年代就是電腦科學的「古典時代」。這十年風起雲湧:IBM 推出了劃時代的 System/360 主機,實現了軟硬體兼容;COBOL 和 FORTRAN 語言開始統治商業與科學計算;人類憑藉阿波羅計畫的導引電腦登上了月球;而「作業系統」(Operating System) 這個概念的出現,更意味著電腦不再是次只做一件事的機器,而是需要同時管理多個任務的複雜中樞。

隨著軟體規模的擴大,程式設計師們的「痛苦」也隨之加劇。他們發現,光靠「陣列」和「鏈結串列」這兩把石器時代的斧頭,已經無法建造起宏偉的軟體大教堂。問題變得更尖銳了:

  • 編譯器如何理解 FORTRAN 程式碼的複雜巢狀結構?
  • 作業系統如何管理上百個檔案的層級目錄?
  • 資料庫(當時的雛形)如何從一百萬筆員工紀錄中,快速找到「Ho Tse Wen」的薪資?

這些問題,都指向了比「線性儲存」更高級的需求——「關係」的表達與「效率」的極致。正是在這個需求背景下,電腦科學正式分野。一群學者試圖為其建立數學般的嚴謹理論;而另一群工程師,則在記憶體的限制中,尋找通往速度的捷徑。

一、 理論的奠基:高德納 (Donald Knuth) 與《電腦程式設計藝術》

1. 一個領域的「聖經」

在 1960 年代之前,程式設計更像是一種「手藝」或「黑魔法」。程式設計師們各自憑藉經驗和直覺,寫出各種巧妙(或糟糕)的程式碼。這個領域缺乏系統性的理論,沒有共同的語言來分析「好」與「壞」。直到一位名叫高德納 (Donald Knuth) 的年輕數學家出現,一切都改變了。1962 年,他開始著手撰寫一本關於「編譯器」的書,但很快發現,在談論編譯器之前,必須先釐清「程式設計」本身的基本原理。

這個計畫最終演變成一套鴻篇巨著——《電腦程式設計藝術》(The Art of Computer Programming, TAOCP)。1968 年,第一卷《基本演算法》(Fundamental Algorithms) 出版。這本書的問世,其意義不亞於牛頓的《自然哲學的數學原理》之於物理學。它首次將程式設計從「手藝」提升到了「科學」的高度。高德納用嚴謹的數學工具,系統性地分析了演算法的效率。

他並未「發明」大部分的資料結構,但他扮演了更關鍵的角色:「定義者」與「分析者」。他引入(或至少是普及了)「大O表示法」(Big O notation),讓程式設計師終於有了一把客觀的尺子,去衡量一個演算法在處理大量資料時的「時間複雜度」和「空間複雜度」。這本書成為了往後數十年電腦科學系學生的「聖經」,它確立了「演算法分析」這門學科的核心地位。

2. 抽象化:從「怎麼存」到「是什麼」

高德納在 TAOCP 中的最大貢獻之一,就是將資料結構「抽象化」。他不再把「鏈結串列」僅僅看作是「節點+指標」的實作技巧,而是將其定義為一種「線性列表」(List) 的抽象概念,並分析其「搜尋」、「插入」、「刪除」等操作的數學特性。同理,他也嚴謹地定義了「堆疊」(Stack,後進先出) 和「佇列」(Queue,先進先出) 這些在 50 年代末已零星出現的結構。

這種抽象化,是電腦科學的第一次思想飛躍。它意味著程式設計師可以先專注於「邏輯」層面:我需要一個什麼樣的結構來解決我的問題? 是需要「後進先出」(例如編譯器在解析函式呼叫時),還是「先進先出」(例如作業系統在管理列印任務時)?決定了邏輯需求後,才去考慮「實作」層面:我該用「陣列」還是「鏈結串列」來實作這個堆疊?

高德納的嚴謹分析,將資料結構從硬體記憶體的束縛中解放出來。他關心的是這些結構的「邏輯特性」與「數學效率」,而非它們在某台特定機器上的位元排列。正是這種對抽象邏輯的重視,為「理論派」開闢了廣闊的天地,使他們得以從古老的數學分支中汲取靈感。

二、 關係的具象化:「樹」(Tree) 與「圖」(Graph) 的引入

1. 超越線性:用「樹」表達層級

1960 年代的程式設計師面臨的最大困境,是如何表達「非線性」的關係。世界並不是一條直線。家族有族譜,公司有組織圖,文章有章節,檔案系統有目錄和子目錄。這些都是「層級」(Hierarchy) 關係。使用「陣列」或「鏈結串列」來儲存這些資料,會變得極其複雜且效率低下。

為此,理論家們從數學的「圖論」(Graph Theory) 中借用了「樹」(Tree) 結構。樹狀結構由「節點」(Node) 和「邊」(Edge) 組成,它有一個頂端的「根節點」(Root),每個節點可以有多個「子節點」,但除了根節點外,每個節點都只有一個「父節點」。這種「一對多」的層級特性,完美地對應了現實世界中的許多問題。

在 1960 年代,「樹」結構迅速在兩個關鍵領域紮根。第一個是編譯器:程式碼中的數學表達式(如 (a + b) * c)或程式區塊(如 if-then-else),可以被完美地轉換成一棵「抽象語法樹」(Abstract Syntax Tree, AST),編譯器可以透過遍歷這棵樹來理解程式的邏輯。第二個是檔案系統:早期的作業系統(如 Multics,Unix 的前身)開始使用樹狀結構來組織檔案,創造了我們今天所熟知的「目錄」和「子目錄」概念。

2. 萬物相連:「圖」的終極抽象

如果說「樹」是「圖」的一個特例(一種沒有迴圈的、連通的圖),那麼「圖」(Graph) 結構就是對「關係」的終極抽象。「圖」只由兩種東西構成:代表「實體」的「頂點」(Vertex) 和代表「關係」的「邊」(Edge)。它既可以表達「樹」那樣的層級關係,也可以表達更複雜的「多對多」網路關係。

在 1960 年代,「圖」的應用在理論上更為活躍。它被用於「作業研究」(Operations Research) 領域,解決諸如「如何規劃最短的送貨路徑」(Dijkstra 演算法,1959 年底提出,60 年代普及)或「如何在網路中找到最大流量」等問題。它也被用於電子電路設計(EDA),模擬晶片上不同元件的連接。

這個時期,「圖」在很大程度上仍是「邏輯模型」的巔峰。它是一種用來分析問題的數學工具,而不是一種常見的儲存資料的工程結構(因為它在記憶體中實作起來相對複雜)。程式設計師使用「圖論」來思考,但他們在實作時,往往需要更接地氣、更關心記憶體效率的工具。這就催生了「應用派」的偉大發明。

三、 效率的混合體:二元樹的應用

1. 搜尋的兩難:陣列 vs. 鏈結串列

「應用派」工程師面臨一個尖銳的矛盾。當資料被儲存在一個「已排序的陣列」中時,使用「二分搜尋法」(Binary Search) 查找非常快,時間複雜度是 O(log n)。但是,「插入」一個新元素卻是一場災難,因為你可能需要移動陣列中的一半元素O(n)。反之,「鏈結串列」插入很快O(1),但搜尋卻只能從頭到尾一個個找,慢如蝸牛O(n)。

工程師們渴望一種「魚與熊掌兼得」的結構:既能像「二分搜尋」一樣快速查找,又能像「鏈結串列」一樣快速插入和刪除。答案,就藏在「樹」結構中。1962 年,P. F. Windley、A. J. T. Colin 和 T. N. Hibbard 等人分別獨立提出了「二元搜尋樹」(Binary Search Tree, BST) 的概念。

BST 是一種特殊的「二元樹」(每個節點最多有兩個子節點)。它規定:對於任何一個節點,其「左子樹」上所有節點的值,都必須「小於」該節點的值;而其「右子樹」上所有節點的值,都必須「大於」該節點的值。這個簡單的規則,卻創造了奇蹟。在 BST 中「插入」一個新元素,就像在樹中「搜尋」它的位置一樣,你只需要從根節點開始,比大小,然後往左或往右走,直到找到空位插入即可。這個過程的平均時間複雜度是 O(log n)。

2. 排序與優先權:「堆積」(Heap) 的誕生

BST 解決了「動態搜尋」的問題,但還有另一個問題:排序。更準確地說,是「優先權」問題。在 1960 年代的作業系統中,多個程式(行程)需要共享 CPU。當 CPU 空閒時,系統該選擇哪個行程來執行?顯然,應該選擇「優先權最高」的那個。這就催生了「優先權佇列」(Priority Queue) 的需求:它需要能快速「插入」一個新任務,並且能隨時「取出」優先權最高的任務。

1964 年,J. W. J. Williams 發明了「堆積」(Heap) 結構,並在同年提出了著名的「堆積排序」(HeapSort) 演算法。Heap 又是「二元樹」的一種巧妙應用。它規定:任何一個「父節點」的值,都必須「大於等於」(或小於等於)其所有「子節點」的值。這被稱為「堆積屬性」。

這個屬性精妙地保證了「根節點」永遠是最大(或最小)的那個元素。因此,「取出最高優先權」的操作,永遠是 O(1) 的(只需抓取根節點)。取出後,為了維持堆積屬性,需要進行一番調整(稱為 heapify),這個調整的時間是 O(log n)。而「插入」一個新元素,也只需要 $O(log n) 的時間。Heap 完美地解決了優先權佇列的問題,至今仍是作業系統排程器的核心結構之一。

3. 邏輯與儲存的首次交會

二元搜尋樹 (BST) 和堆積 (Heap),是 1960 年代最偉大的發明之一。它們的誕生,標誌著「邏輯模型」與「儲存模型」的第一次完美交會。它們都是「樹」這種抽象邏輯結構,但它們被設計出來的唯一目的,是為了解決「搜尋」和「排序」這兩個最核心的「儲存效率」問題。

然而,BST 很快暴露出一個嚴重缺陷:它在「平均情況」下表現良好 ($O(\log n)$),但在「最壞情況」下(例如,當你按順序插入 1, 2, 3, 4, 5...),BST 會「退化」成一條傾斜的「鏈結串列」,其所有操作的效率都會掉到 $O(n)$。這個「平衡」問題,成為了 60 年代末的未解難題(如 AVL 樹在 1962 年被提出,但尚未普及),也直接引爆了 70 年代「平衡樹」的黃金年代。

四、 效率的極致:雜湊表 (Hash Table) 的勝利

  1. O(1) 的終極夢想

當理論家們還在為 BST 的 O(log n) 效率歡呼時,一群更講求「實用」和「速度」的工程師,正在追求一個更瘋狂的夢想:我們能不能像「陣列」一樣,在 $O(1)$(常數時間)內,完成搜尋、插入和刪除? 換句話說,我不管資料有多少(一千筆或一億筆),我都要在「一步」之內找到它。

這個夢想聽起來像天方夜譚。陣列之所以能 O(1) 存取,是因為資料的「鍵」(Key)(即它的索引 index)和它的「儲存位置」(Address) 是一致的。但如果我的鍵是「Tse Wen」這樣的字串呢?我總不能在記憶體中創造一個「Tse Wen」號地址吧?

「雜湊表」(Hash Table) 的發明,就是為了實現這個夢想。這個概念的起源可以追溯到 1953 年 IBM 的 Hans Peter Luhn,但它真正在 1960 年代被工程師們發揚光大。其核心思想被稱為「雜湊」(Hashing):使用一個「雜湊函式」(Hash Function),將任意的「鍵」(Key) 轉換成一個固定範圍的「整數」,並以此整數作為「陣列」的「索引」

2. 魔法的代價:「碰撞」(Collision)

雜湊表的運作原理,就像一個魔法分類帽。當一筆新資料(例如,學號 S123456,姓名 Ho Tse Wen)進來時,你只看它的鍵 S123456。你把這個鍵丟進「雜湊函式」(例如,f(key) = key % 997),函式吐出一個數字,比如 123。於是,你就把 Ho Tse Wen 的資料,儲存到一個大陣列的第 123 號位置。

下次,當你要「搜尋」S123456 時,你根本不需要遍歷。你只需要重複一次這個計算,f(S123456) = 123,然後直奔陣列的第 123 號位置,資料就在那裡。這就是 $O(1)$ 的魔力。這使得雜湊表成為 1960 年代「編譯器」開發者的最愛。編譯器需要一張「符號表」(Symbol Table) 來儲存程式中所有的變數名稱(如 x, temp, my_function),它需要極快地查詢這些變數的資訊,雜湊表是完美的不二之選。

然而,這個魔法是有代價的。如果 S123456 算出來是 123,而另一筆資料 S654321 算出來也是 123 怎麼辦?這就是「碰撞」(Collision)。1960 年代的工程師們圍繞「碰撞」發展了兩大解決策略:一是「鏈結法」(Chaining),在陣列的第 123 號位置掛一個「鏈結串列」,把所有雜湊到 123 的資料都串在後面;二是「開放定址法」(Open Addressing),如果 123 被佔了,就去試 124、125,直到找到空位為止。

3. 「儲存模型」的極致體現

雜湊表是「儲存模型」哲學的極致體現。它在「邏輯」上是混亂的:它完全打亂了資料的原始順序,S123456S123457 可能被儲存在記憶體中兩個相距十萬八千里的位置。它在「空間」上是浪費的:為了減少碰撞,雜湊表通常需要保持 30% 以上的「空閒」空間。

它犧牲了「順序性」和「空間」,只為了換取「速度」這一個目標。它不關心資料的「抽象關係」,只關心資料在「記憶體中的物理位置」。它不是數學家眼中優雅的藝術品,而是工程師手中那把最快、最有效率的鐵鎚。雜湊表的巨大成功,深刻地體現了 1960 年代「實用主義」的工程精神。

五、 雙軌的並行

1960 年代,是電腦科學的「成年禮」。這個十年,以一個巨大的「分歧」作為標誌。

  • 「邏輯模型」(理論派):由高德納集大成,他們關心「抽象」與「數學」。他們從圖論中帶來了「樹」和「圖」,試圖為世界的「關係」建模。他們追求的是嚴謹、優雅與「可分析性」。
  • 「儲存模型」(應用派):由無數的系統工程師推動,他們關心「效率」與「硬體」。他們在記憶體的限制中,創造了「二元搜尋樹」、「堆積」和「雜湊表」。他們追求的是速度、速度、還是速度。

這兩條軌道,看似背道而馳,實則缺一不可。沒有「邏KA輯模型」的抽象,程式設計將永遠停留在手工作坊階段,無法建造複雜的系統;沒有「儲存模型」的效率,再優雅的理論也無法在物理硬體上真正運行。

1960 年代奠定了這兩條軌道。進入 1970 年代,這兩條軌道將開始更深層的交織。理論家們將著手解決 BST 的「平衡」問題,而工程師們則會把這些結構「封裝」起來,一場名為「抽象資料型態」(ADT) 的革命,即將來臨。

留言
avatar-img
留言分享你的想法!
avatar-img
政大雜學筆記
0會員
26內容數
我在政大的一些紀錄
政大雜學筆記的其他內容
2025/11/05
在 1950 年代,電腦是重達數噸、處理能力不如現代計算機的巨型機械。在那個「資料結構」一詞尚不存在的年代,程式設計師只有一個最直觀的工具:陣列 (Array),它將資料如士兵般整齊排列。然而,當 Allen Newell 與 Herbert Simon 試圖打造能「思考」的程式時,這套系統崩潰了。
2025/11/05
在 1950 年代,電腦是重達數噸、處理能力不如現代計算機的巨型機械。在那個「資料結構」一詞尚不存在的年代,程式設計師只有一個最直觀的工具:陣列 (Array),它將資料如士兵般整齊排列。然而,當 Allen Newell 與 Herbert Simon 試圖打造能「思考」的程式時,這套系統崩潰了。
2025/11/05
「快取未命中 」是 CPU 的效能殺手。當 CPU 在高速小冰箱(快取)中找不到資料,就必須停工,去慢速大倉庫(RAM)取貨,這會導致 100 倍的延遲。三種主要元兇:不可避免的「強制性失誤」(冷啟動)、因資料太多導致的「容量性失誤」(冰箱太小),以及最棘手的「衝突性失誤」(儲存規則衝突)。
2025/11/05
「快取未命中 」是 CPU 的效能殺手。當 CPU 在高速小冰箱(快取)中找不到資料,就必須停工,去慢速大倉庫(RAM)取貨,這會導致 100 倍的延遲。三種主要元兇:不可避免的「強制性失誤」(冷啟動)、因資料太多導致的「容量性失誤」(冰箱太小),以及最棘手的「衝突性失誤」(儲存規則衝突)。
2025/11/05
BST 是 O(log N) 搜尋的「理論原型」,但輸入有序時會退化成 O(N)。Treap 巧妙地加入「隨機優先級」,利用機率來維持樹的統計平衡,是對演算法的優化。B-Tree 則是為了解決硬碟 I/O 緩慢的「硬體現實」而生,它使用極度「矮胖」的巨大節點,大幅降低樹高,是現代資料庫的基石。
2025/11/05
BST 是 O(log N) 搜尋的「理論原型」,但輸入有序時會退化成 O(N)。Treap 巧妙地加入「隨機優先級」,利用機率來維持樹的統計平衡,是對演算法的優化。B-Tree 則是為了解決硬碟 I/O 緩慢的「硬體現實」而生,它使用極度「矮胖」的巨大節點,大幅降低樹高,是現代資料庫的基石。
看更多
你可能也想看
Thumbnail
身為新手媽媽,育兒生活讓你無法逛街?別擔心!本文精選多款網購必備母嬰用品,包含寶寶粥、尿布、玩具、童書、衣物和育成椅,並分享實用的省錢購物技巧,讓你輕鬆購得好物,享受聰明網購樂趣。另有蝦皮雙11購物節與分潤計畫介紹,幫助你省荷包,開創斜槓收入。
Thumbnail
身為新手媽媽,育兒生活讓你無法逛街?別擔心!本文精選多款網購必備母嬰用品,包含寶寶粥、尿布、玩具、童書、衣物和育成椅,並分享實用的省錢購物技巧,讓你輕鬆購得好物,享受聰明網購樂趣。另有蝦皮雙11購物節與分潤計畫介紹,幫助你省荷包,開創斜槓收入。
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開始講起。 定義
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News