在 20 世紀中葉,人類剛從第二次世界大戰的硝煙中走出,卻旋即投入了冷戰的科技競賽。這個時代的「電腦」,是充滿了真空管、繼電器和複雜線路的龐然大物。它們佔據整個房間,需要專門的冷卻系統,其運算能力甚至遠遜於你口袋中的智慧型手機。例如 1946 年的 ENIAC 或 1951 年的 UNIVAC,它們的「程式設計」更像是「接線工程」,而「資料」則是儲存在打孔卡或磁鼓上的一連串數字。
當時的電腦,本質上是超級計算機。它們被設計來解決特定的、龐大的數學問題:計算火砲彈道、破解密碼、處理人口普查。在這個脈絡下,「資料結構」並不是一個被討論的概念,它更像是一種隱含的假設。資料就是用來計算的數字,而儲存它們最合乎邏S輯的方式,就是把它們一個個緊密地排列起來。
然而,風暴正在醞釀。一小群先驅者,不滿足於讓電腦僅僅作為計算工具。他們有一個更宏大的夢想:讓機器模擬人類的思維。這個夢想將迫使電腦走出數字計算的舒適圈,去處理一個更棘手的對象——「邏輯」與「符號」。而正是這個轉變,催生了資料結構領域的第一次大革命,劃開了電腦科學的「創世紀」。
一、 洪荒年代:陣列 (Array) 的統治
1. 記憶體的「物理直覺」
在 1950 年代,電腦的記憶體(例如磁芯記憶體)是極其珍貴且昂貴的資源。工程師必須以最節省的方式使用每一「位元」。當時的記憶體在物理上就是一個線性的、連續的地址序列。當科學家和工程師(當時還沒有「程式設計師」這個專業分工)需要儲存一組數據,比如 100 個氣象觀測站的溫度讀數時,最自然、最直接的想法是什麼?答案顯而易見:在記憶體中找一塊連續的空地,然後把這 100 個數字一個挨一個地放進去。
這就是「陣列」(Array) 的原型。它與其說是被「發明」的,不如說是從硬體特性中「自然生長」出來的。它將抽象的資料(一組溫度)與具體的記憶體物理地址(例如從 1001 號到 1100 號)進行了最簡單的 1:1 映射。這種結構的優勢是壓倒性的:它非常快。如果你想知道第 50 個觀測站的溫度,你只需要用「起始地址 + 50」就能立刻「跳」到該位置,這就是日後我們所說的 O(1)「常數時間存取」。
因此,在 FORTRAN(1957 年誕生,意為「公式翻譯」)這樣的早期科學計算語言中,陣列是其核心的、乃至唯一的複雜資料組織方式。對於當時的數值計算、矩陣運算或統計分析而言,陣列不僅足夠好,而且是完美的。它結構簡單、硬體親和性高、存取迅捷,完美體現了那個時代對「效率」的極致追求。
2. 「靜態」的黃金枷鎖
然而,陣列的完美,是建立在一個脆弱的假設之上的:你必須事先知道你需要多少空間。在你儲存 100 個溫度讀數之前,你必須先向系統申請「100 個單位的連續空間」。這就是陣列「靜態」(Static) 特性的本質。在那個記憶體以 KB 計算的年代,你不能隨意浪費,申請多了是奢侈;但你也不能申請少了,因為一旦資料來了,空間卻不夠,麻煩就大了。
「擴充」一個陣列在當時是一場災難。假設你的 100 個單位的陣列滿了,但又來了第 101 個數據。由於陣列要求「連續」,而 1101 號地址可能已經被其他程式佔用,你唯一的辦法就是:另外找一個能容納 101 個(或更多,例如 200 個)單位的「全新」連續空間,然後把舊陣列的 100 個數據「逐一複製」過去,最後再把第 101 個數據放進新家。這個過程極其緩慢,且在記憶體極度碎片化的情況下,要找到一塊足夠大的新空間本身就是個難題。
這種「靜態」特性,就像一副黃金枷鎖。它在處理數字矩陣時光彩奪目,但在面對那些「未知長度」、「需要頻繁增刪」的問題時,則顯得笨拙不堪。例如,處理一串文字(字串),或模擬一個家族的族譜。當問題從「計算」轉向「關係」時,陣列的侷限性便暴露無遺。電腦科學需要一種新的工具,一種能掙脫這副枷鎖的工具。
二、 邏輯的火花:Newell, Simon 與「會思考的機器」
1. 跨界的夢想家:紐厄爾 (Newell) 與西蒙 (Simon)
這場革命的推動者,出乎意料地,並非來自傳統的數學或工程領域。艾倫·紐厄爾 (Allen Newell) 是一位物理學家,在二戰後加入了以軍事研究聞名的「蘭德公司」(RAND Corporation)。赫伯特·西蒙 (Herbert Simon) 則更為傳奇,他是一位政治學家和經濟學家(他後來在 1978 年獲得了諾貝爾經濟學獎)。他們兩人的共同點是,他們都著迷於人類的「決策」與「解題」過程。
在 1950 年代中期,他們在蘭德公司相遇,當時的蘭德公司正試圖將新生的電腦技術用於軍事策略和組織行為研究。紐厄爾和西蒙很快確立了一個遠大的目標:他們不滿足於讓電腦計算彈道,他們要讓電腦「模擬人類的認知過程」。他們相信,人類的思考,本質上也是一種「資訊處理」,因此,電腦沒有理由不能「思考」。
這個在當時看來近乎科幻的構想,是「人工智慧」(AI) 領域的濫觴。他們選擇的突破口,是證明懷特海與羅素的巨著《數學原理》中的邏輯定理。他們要打造的,不是一個「計算機」,而是一個「邏輯理論家」(Logic Theorist)。這個專案將直接挑戰當時電腦硬體和程式設計思想的極限。
2. 「邏輯理論家」的困境
1956 年,紐厄爾、西蒙以及他們的同事克里夫·蕭 (Cliff Shaw) 開始著手「邏輯理論家」專案。他們很快發現,傳統的 FORTRAN 和陣列根本無法使用。為什麼?因為「證明一個定理」的過程,和「計算一組數字」的過程,在根本上是不同的。一個邏輯證明,更像是在探索一棵迷宮般的「決策樹」。
想像一下,你要證明「如果 A 成立,則 B 成立」。你可能會嘗試「反證法」:假設 A 成立而 B 不成立,看能否推導出矛盾。這就產生了一個「分支」。在這個分支下,你可能又會引用公理 C,產生新的分支。你無法預知這個證明需要多少步驟,也無法預知你會探索多少條「死路」。資料(即邏輯語句)是動態產生、增長、甚至被拋棄的。
陣列的靜態特性在此刻成了致命弱點。你不可能預先申請一個「剛好能放下整個證明過程」的記憶體空間。你需要的,是一種可以「隨用隨取」、隨時「長出新節點」、也能隨時「剪掉無用分支」的資料結構。當他們發現現存的所有工具都無法滿足需求時,他們決定——自己創造一個。
三、 鏈結串列 (Linked List) 的誕生
1. 解放記憶體:節點 (Node) 與指標 (Pointer)
紐厄爾和西蒙(以及負責主要實作的蕭)的構想是革命性的。他們提出:為什麼資料一定要儲存在「連續」的空間裡? 記憶體的地址是零散的,只要我們有辦法把它們「串」起來,不就可以利用所有碎片空間了嗎?於是,「鏈結串列」(Linked List) 誕生了。
這個想法的核心是「節點」(Node)。他們把資料拆分成最小單元,每個單元就是一個節點。這個節點儲存在記憶體中任何一個可用的位置。關鍵在於,這個節點包含兩個部分:第一部分是「資料」本身(例如,一句邏輯公理);第二部分則是一個「指標」(Pointer),這個指標儲存的,是「下一個」節點的記憶體地址。
就這樣,一個完整的資料列表,就像一串珍珠項鍊。你只需要握住第一顆珍珠(稱為「頭節點」 Head),就能透過它上面的線(指標),找到第二顆珍珠;再透過第二顆的線,找到第三顆……直到最後一個節點,它的指標指向一個特殊的「空值」(Null),表示串列結束。這種「隨處儲存、彼此相連」的設計,徹底解放了記憶體。
2. 隨心所欲的「動態」
鏈結串列的威力,在於它無與倫比的「動態」靈活性。當「邏輯理論家」需要推導出一個新的邏輯語句時,它只需要向作業系統(當時還很原始)索取「任一塊」可用的記憶體空間來建立一個新節點。然後,它只需要把這個新節點「插入」到現有的邏輯鏈中——這個操作僅僅需要修改一到兩個「指標」的指向,而不需要像陣列那樣移動成千上萬的資料。
例如,要在 A 和 B 之間插入 C,只需要:1. 把 C 的指標指向 B;2. 把 A 的指標從 B 改為指向 C。完成了!這個操作的速度,與串列有多長(無論是 10 個還是 10 萬個)完全無關,這就是 O(1)「常數時間插入」。同理,刪除一個節點也只是修改一個指標,讓前一個節點「跳過」被刪除的節點,直接指向下下個節點即可。
為了實作這個構想,他們甚至設計了專門的程式語言——IPL (Information Processing Language),這被認為是 LISP 語言(AI 領域的傳奇語言)的前身。IPL 語言的核心就是處理這種「列表」。紐厄爾和西蒙的發明,讓程式第一次擁有了「動態成長」和「自我重組」的能力,這對於 AI、符號計算、乃至後來的作業系統(管理進程)都至關重要。
四、 第一次分歧:靜態 vs. 動態的永恆權衡
1. 效率的兩種定義
1950 年代末,隨著陣列和鏈結串列的相繼登場,電腦科學史上第一個偉大的「分歧點」出現了。這不僅是兩種技術的選擇,更是兩種哲學的碰撞:靜態的、可預測的世界,對決動態的、靈活的世界。
「陣列」代表了「靜態分配」。它的優勢在於「存取效率」。由於記憶體連續,你可以用一個簡單的數學公式(基底位址 + 索引 × 單位大小)瞬間跳到任何一個元素。但它以「犧牲彈性」為代價。它適用於那些結構穩定、大小已知、讀取遠多於修改的場景,例如科學計算中的矩陣、資料庫中的固定欄位記錄。
「鏈結串列」代表了「動態分配」。它的優勢在於「增刪效率」。你可以在不移動任何現有資料的情況下,快速插入或刪除節點。但它以「犧牲存取」為代價。因為節點在記憶體中是隨機散布的,你無法「跳」到第 50 個元素,你只能從頭節點開始,一步一步「走」過 49 個指標才能到達,這就是 O(n)「線性時間存取」。
2. 永恆的權衡 (Trade-off)
這個「靜態 vs. 動態」的分歧,成為了軟體設計中永恆的權衡。直到今天,程式設計師在選擇資料結構時,仍在回答 1950 年代就已提出的這個問題。
如果你在開發一個需要極致存取效能的圖形渲染引擎,你可能會傾向於使用陣列來儲存頂點資料。如果你在編寫一個文字編輯器,它的「復原/重做」功能需要頻繁地在操作歷史中插入和刪除步驟,鏈結串列(或其變體)將是更好的選擇。現代的程式語言,如 Python 的 list 或 Java 的 ArrayList,更是試圖在兩者之間取得平衡——它們表面上是動態的,但底層實作卻是「動態調整大小的陣列」,試圖兼顧兩者的優點。
1950 年代,這個電腦還像打字機的年代,以兩種截然不同的方式定義了「資料的形狀」。一個是秩序井然的軍陣(陣列),一個是自由串連的珍珠(鏈結串列)。它們就像是資料結構這棵大樹的兩條主根,為 1960 年代即將到來的「樹」、「圖」、「堆疊」等更繁茂的結構,提供了最根本的養分。這個由邏輯學家和工程師共同開啟的時代,奠定了一切的基礎。
















