① 照節次讀:每一節都短,不要跳著看。 ② 動手驗證:有互動元件的地方先自己預測答案,再按按鈕對照。 ③ 做完自我檢核再往下:答錯就回頭重讀該節。 ④ 最後翻 關鍵詞彙卡,能不看答案講出定義才算過關。
這個問題值得認真回答,而不是用「基本功很重要」六個字打發掉。
先承認事實:把「幫我用 C++ 實作一棵二元搜尋樹,含插入、搜尋、刪除」貼給任何一個主流模型,你會在幾秒內拿到一份看起來很專業、而且大部分情況真的會編譯過的程式碼。這門課教的每一個資料結構,AI 都寫得出來。
所以真正的問題只有一個:換你來當那個要為結果負責的人時,你憑什麼判斷它寫得對不對。
AI 讓「產出程式碼」變得幾乎免費,價值就整個移到另一端:選擇、驗證與取捨。這三件事剛好就是資料結構與演算法在教的東西。而 C++ 還多一層 —— 記憶體要你自己管,AI 幫你漏掉的 delete 不會有人提醒你。
你要 AI 排序,它給你一段程式。編譯過了,用 20 筆資料測試,0.001 秒,完美。三個月後資料變成一百萬筆,同一段程式跑了四十分鐘還沒結束。
AI 沒有寫錯。你沒說要多快,它也不知道你的 n 會長多大。而「n 長大時會怎樣」正是這門課第 02 章要教的東西:漸近複雜度。
| 成長級數 | 典型代表 | 操作次數 | 估計時間 (每秒 10⁸ 次操作) |
|---|
n 小的時候,所有演算法看起來都一樣快。這正是「用小資料測試」會騙人的原因。
這個差距要用數量級來算:n = 100 萬時,$O(n^2)$ 要花的時間是 $O(n \log n)$ 的五萬倍。到這個地步就不是效能調校的問題了,是能不能做的問題。
第 02 章〈演算法分析〉會帶你用 <chrono> 實際量出這張表,而不是只看理論曲線。
AI 不會主動告訴你「你選的這個做法在 n 很大時會爆炸」,除非你問。而你要先知道有這回事,才問得出來。
下面兩個都是真實常見的狀況:程式完全正確、測試全過、AI 也覺得沒問題,但效能是災難。而且這兩個坑,課程自己都準備好了教材。
課程的 pythonds3/cppds/stack.hpp 裡放了兩個堆疊:Stack 把頂端放在 vector 的尾端,Stack2 放在前端。兩者都提供後進先出的堆疊操作。
Stack (top 在尾端): 1.13921 ms Stack2 (top 在前端): 122.423 ms
在 vector 的前端插入或刪除,都需要搬移後續元素,單次操作是 $O(n)$。尾端插入是攤還 $O(1)$,尾端刪除是 $O(1)$。執行 Stack 與 Stack2,比較相同堆疊操作在兩種實作中的耗時。
vector 逐一比對: 102.673 ms unordered_set 雜湊查表: 0.006698 ms
vector 的搜尋逐一比對元素;unordered_set 利用雜湊值定位,在雜湊分布良好、負載因子受控時,平均查詢成本為 O(1),最差為 O(n)。第 07 章會說明雜湊表如何完成查詢。
編譯不過你至少知道要修。但「跑得出正確答案、資料增加後越跑越慢」不會噴任何訊息,它只會在你上線之後、資料長大之後才開始傷人。要抓到這種問題,你必須看得懂那段程式在做什麼。
第 02 章教你怎麼估這些成本;第 05 章的 Stack、Queue、Deque 全部在做「選哪一端」的決定;第 07 章則是把 unordered_set 背後的雜湊表拆開來自己刻一遍。
這一節是 C++ 學生獨有的。用 Python 的人不會遇到,因為那邊有垃圾回收;在 C++,new 出來的東西你不還就永遠不會回來。
從鏈結串列到樹,新增節點時都要安排它的生命週期:誰建立、誰持有,以及何時釋放。
| 資料結構 | 建立什麼 | 何時釋放 |
|---|---|---|
| 鏈結串列 | 儲存資料與連結的節點 | 刪除節點,或銷毀整條串列時 |
| 鏈結實作的堆疊與佇列 | 存放元素的節點 | 移除元素,或銷毀容器時 |
| 圖 | 頂點與邊的資料 | 由擁有這些資料的物件管理 |
| 樹 | 儲存鍵與子節點連結的節點 | 刪除節點,或銷毀整棵樹時 |
一開始還活著的節點: 0 buildAndFree 之後: 0 <- 借兩個、還兩個,歸零 buildAndForget 之後: 2 <- 借兩個、沒還,永遠回不來了 再呼叫三次之後 : 8 <- 每呼叫一次就漏兩個
Node::alive 記錄目前還活著的節點數。buildAndForget 每呼叫一次就漏兩個,而且漏掉的那塊記憶體在程式結束前拿不回來。這種錯誤不會噴訊息,只會讓長時間執行的程式越吃越多記憶體。
建立動態物件時,先決定誰負責管理它的生命週期。由容器擁有的節點,可在容器的解構子中釋放;使用智慧指標則可讓物件隨擁有者的生命週期自動釋放。先備頁 P4 會把這件事講完整。
還不熟悉 C++ 的基本語法,可以先從P1 的最小程式開始。
回到 AI:你請它「幫我實作一個鏈結串列」,它八成給你一份能跑的程式碼。但有沒有解構式?remove() 之後那個節點 delete 了嗎?它不會主動講,而編譯器也不會抱怨。這是你要看得出來的。
第 04 章的 UnorderedList::remove() 與第 09 章的 BST 刪除,都是「把節點從結構裡摘掉」的操作 —— 摘掉之後那塊記憶體歸誰管,就是這一節在問的問題。
學會一件事的迴圈長這樣:想法 → 動手試 → 觀察結果 → 修正理解。四步缺一不可,而且卡住、debug、恍然大悟的那個瞬間,正是理解真正發生的地方。
直接把題目丟給 AI、拿回答案貼上去編譯,等於把整個迴圈砍掉只剩「觀察結果」。程式會動,作業會過,但你腦袋裡什麼都沒長出來。
照著食譜做菜,你會做出那道菜;但你不會變成廚師。廚師的能力在於:食材換了、火力不同、客人臨時說不吃辣,他還能重新設計一道菜。你想成為哪一種?
這門課裡真正難的從來不是「寫出 quicksort」。難的是「為什麼 quicksort 平均是 $O(n \log n)$、最壞卻是 $O(n^2)$」、「為什麼要選這個 pivot」、「這個指標在遞迴回來之後還指著同一個地方嗎」。這些都是推理,抄一百遍程式碼也長不出來。
能,而且應該用,但用法要對。把 AI 當助教用,不要當代工:先自己寫、卡住了問「我這個 segfault 是怎麼來的」、拿到解釋之後自己改。C++ 的編譯錯誤訊息又長又難讀,請 AI 幫你翻譯錯誤訊息是很好的用法 —— 那是在幫你完成迴圈,不是繞過它。
不管你之後做的是遊戲引擎、嵌入式、高頻交易、影像處理還是科學計算,你都會遇到「資料太多、跑太慢」的那一天。到那天,你會需要雜湊表、堆積、圖的最短路徑這些詞。「再租一台更大的機器」買不了多少時間。
這門課用的 pythonds3/cppds/、你未來會用的 STL、Boost、Eigen,全部是別人寫的資料結構。C++ 的文件出了名地難讀,而 template 的錯誤訊息更是一長串。當文件講不清楚,唯一的辦法就是打開標頭檔看。而標頭檔裡面就是 class、就是指標、就是樹和圖。
當產出程式碼變得免費,會產出的人就不值錢了。真正變貴的是能審查的人:能看一眼就說「這裡會 $O(n^2)$」「這個 new 沒有配對的 delete」「這個迴圈裡 push_back 之後 iterator 就失效了」。技術面試考白板題、團隊要人 code review,考的都是這一層。
AI 會放大你的能力,也會放大你的無知。懂的人用它跑得更快;不懂的人用它把錯誤生產得更快。
① 先估 Big-O,再動手寫。拿到題目先問自己:n 會多大?我這個做法是幾次方?光是養成這個反射,你就贏過一半的人。
② 先預測輸出,再按執行。本站每一個互動元件都是為了這件事設計的:先在心裡(或紙上)寫下你認為的答案,再按按鈕。猜錯的那一刻,才是你真正學到東西的一刻。
③ 問「為什麼」而不是「幫我寫」。把「幫我實作 AVL 樹」換成「我的旋轉寫在這裡,為什麼平衡因子沒有更新?」—— 前者你拿到程式碼,後者你拿到理解。
④ 每寫一個 new,當場找到它的 delete。不是寫完再回頭補,是當下就想清楚「這塊記憶體誰負責還、什麼時候還」。想不清楚,就是你的設計還沒想好。
⑤ 交出去之前逐行讀自己的程式。如果有任何一行你說不出「這行在幹嘛、拿掉會怎樣」,那行就不算你的。
讀完這頁,先去 00B · 課前準備與環境安裝 把 compiler 與 notebook 環境弄好;Windows 作業再完成 00C · VS Code 作業實戰,然後從 第 01 章 開始。如果你對 C++ 本身還不太有把握,先掃一遍 先備頁 P1–P9(選讀,不評分)—— 尤其是 P4 的指標與動態記憶體,第 04 章以後幾乎每一頁都要用到。
一段程式在 1,000 筆資料上跑 0.01 秒。如果它是 $O(n^2)$,資料變成 100,000 筆時大約要多久?
AI 幫你寫了一個鏈結串列,remove() 的實作是把前一個節點的 next 接到後一個節點,然後 return。編譯過了,測試也過了。有問題嗎?
下面哪一種用 AI 的方式,最能幫助你真的學會這門課?
| 資源 | 為什麼值得看 |
|---|---|
| Martin Fowler — Exploring Generative AI | 軟體工程視角看生成式 AI:什麼場合適合、什麼場合會付出長期維護代價。 |
| cppds(本課教科書) | Problem Solving with Algorithms and Data Structures using C++,本站每章 §徽章都對應此書節號。 |
| cppreference · Containers | STL 各容器操作的複雜度保證,官方權威。想知道 vector::insert 為什麼慢,這裡有答案。 |
| C++ Tutor | 逐行視覺化記憶體與指標變化。看不懂指標或遞迴時,貼進去按下一步。 |
| nsysu-math208 | 課程講義與投影片 repo,本站是它的互動版配套。 |
先看正面術語,心中默想定義再翻面對答案;洗牌後再過一輪,直到每張都能不假思索說出來。