面試準備#
技術面試不只是考察演算法能力,更是觀察你的思維方式與溝通能力。本章整理面試準備的核心策略。
切題四件套#
當面試官提出問題時,請遵循以下四個步驟,將其內化為條件反射:
flowchart LR
A[1. Clarification<br/>釐清需求] --> B[2. Possible Solutions<br/>探討解法]
B --> C[3. Coding<br/>撰寫程式碼]
C --> D[4. Testing<br/>測試驗證]
style A fill:#e1f5fe
style B fill:#fff3e0
style C fill:#e8f5e9
style D fill:#fce4ecStep 1: Clarification(釐清需求)#
不要急著解題。首先與面試官溝通,確認題目細節:
- 詢問邊界條件(Edge cases)
- 確認輸入資料的規模與限制
- 探討可能的極端錯誤情況
Step 2: Possible Solutions(探討解法)#
千萬不要一想到某個解法就立刻開始寫程式碼。
- 全面思考:腦力激盪出所有可能的解法
- 比較分析:評估不同解法的時間與空間複雜度
- 選擇最佳:通常選擇時間最快、記憶體最省、或程式碼最優雅的解法
- 溝通確認:動手前先與面試官確認解法是否符合預期
Step 3: Coding(撰寫程式碼)#
- 採用標準化、模組化的寫法
- 應用平時練習累積的程式碼模板
- 保持程式碼的整潔與可讀性
Step 4: Testing(測試驗證)#
寫完程式碼後,主動進行測試:
- 正常資料:一般情況的測試
- 邊界資料:極端情況(空值、最大/最小值)
- 特殊資料:可能導致錯誤的陰險測資
白板解題七步流程#
上面的「切題四件套」是骨架。在白板或共享編輯器上實戰時,把它展開成更細的七個步驟,能讓你不漏掉關鍵動作、也讓面試官看見你完整的思考軌跡。
| 步驟 | 動作 | 重點與常見陷阱 |
|---|---|---|
| 1. Listen(聽清楚) | 聽完整題目,記下每一個獨特條件 | 像「陣列已排序」「同一個函式會被重複呼叫」這類字眼往往是提示:暗示你可以用二分搜尋,或該預先計算/快取結果 |
| 2. Draw Example(畫例子) | 手畫一個夠大、非特例的具體例子 | 別用 [1, 2] 這種太小或太規律的例子,容易誤導你寫出只對特例成立的解 |
| 3. Brute Force(暴力解) | 先講出最直白的暴力解,並給出它的複雜度 | 不要因為它慢就跳過。先有一個能跑的解,面試官會知道你至少能解,後面再優化 |
| 4. Optimize(優化) | 用 BUD 等方法系統性地找優化空間 | 對著暴力解逐項問:瓶頸在哪?哪些是多餘的工作?哪些重算了? |
| 5. Walk Through(走通) | 動手前,先在腦中或口頭把演算法走一遍 | 急著下筆是大忌。先把邏輯講通,寫的時候才不會邊寫邊改、自亂陣腳 |
| 6. Implement(實作) | 寫整潔、模組化的程式碼 | 命名清楚、把可重用的部分抽成小函式,留給後面測試與除錯的空間 |
| 7. Test(測試) | 自己主動測:正常、邊界、特殊測資 | 從小例子手動代入;發現 bug 時,要有條理地定位,而不是亂改 |
七步之間是可以來回的。例如在第 7 步測出錯,回到第 5 步重新走通邏輯,是完全正常的;面試官看重的是這套有紀律的流程,而不是一次到位。
BUD 優化法#
當你有了暴力解、要往第 4 步(Optimize)推進時,BUD 提供三個明確的著力點,讓「優化」不再靠運氣。逐一檢視這三點,往往就能找到下一個更快的解。
| 著力點 | 自問 | 具體例子 |
|---|---|---|
| Bottlenecks(瓶頸) | 整個流程最耗時的那一步是什麼? | 在迴圈裡反覆做線性搜尋(O(n)),改用雜湊表把查找降到 O(1) |
| Unnecessary work(多餘的工作) | 有沒有根本不必做的步驟? | 要找「a + b + c + d = 0」的組合時,固定前三個後第四個可直接算出,不必再開一層迴圈枚舉 |
| Duplicated work(重複的工作) | 同一份計算是不是被算了很多次? | 把會重複用到的中間結果預先存進陣列或快取(這正是動態規劃的核心動機) |
找到瓶頸後,要集中火力消掉那一個瓶頸——把一個
O(1)的步驟優化成更快沒有意義,真正影響整體複雜度的是最慢的那一段。
五大解題技巧#
當暴力解都想不出來、或想不到怎麼優化時,下面五種思路可以輪流嘗試,幫你打破僵局:
- BUD:對著現有解,逐項檢查瓶頸、多餘的工作、重複的工作(見上一節)。
- DIY(Do It Yourself):拿一個具體例子,用直覺手動把答案解出來,再回頭反向工程——觀察「我剛剛腦中是怎麼做到的」,把那個過程翻譯成演算法。
- Simplify & Generalize(先簡化再推廣):先把問題的某個限制拿掉、解出簡化版,再一步步把限制加回去、推廣到原題。
- Base Case and Build(從基礎往上建):先解最小的情況(n = 0, 1, 2),再看 n 的解能否由 n−1 的解推得。這條路常常自然導向遞迴或動態規劃。
- Data Structure Brainstorm(資料結構腦力激盪):把常見資料結構(陣列、雜湊表、堆疊、佇列、堆積、樹、圖、Trie……)一個個套上去想,常會突然冒出靈感——「如果用堆積維護當前最大值會怎樣?」
BCR:最佳可能執行時間#
BCR(Best Conceivable Runtime,最佳可能執行時間)指的是:這道題在理論上最快能多快?它其實就是問題的下界——不管演算法多聰明,至少要做的工作量。
BCR 是優化時的指南針:
- 如果你目前的解已經達到 BCR,就可以收手,不必再鑽牛角尖。
- 如果還沒達到,BCR 告訴你「還有空間」,並暗示該消掉哪個瓶頸(往往是讓你超過 BCR 下界的那一步)。
例如:一道題若必須讀過所有 n 筆輸入,BCR 就是 O(n);那麼任何 O(n log n) 的解都還有被壓到 O(n) 的可能。
先問下界,是高手的習慣。 動手優化前先估出 BCR,你才知道目標在哪、什麼時候該停。這套「先找下界」的思維,在 複雜度的下界 與 演算法思維總綱 有更完整的展開。
複雜度溝通#
整個流程中,每講一個解法,都要主動附上它的時間與空間複雜度——這是面試官判斷你功底的最直接訊號,不要等他來問。
組合多個步驟的複雜度時,分清楚兩種情況:
| 情境 | 規則 | 例子 |
|---|---|---|
| 做完 A,再做 B(先後) | 相加 O(A + B) | 先排序 O(n log n),再掃一遍 O(n),合計 O(n log n) |
| 每個 A 都要做一次 B(巢狀) | 相乘 O(A × B) | 對每個元素都做一次線性搜尋,O(n) × O(n) = O(n²) |
講複雜度時順手點出「這裡是兩段先後所以相加」「這裡是巢狀所以相乘」,既能避免自己算錯,也讓面試官看見你對複雜度的掌握是清楚而非死背。
刻意練習策略#
走出舒適區#
當你發現做某類題目感到非常順手時,必須強迫自己進入不熟悉的領域。
許多人會陷入「為了切題而切題」的誤區,只反覆練習熟悉的題目。這能帶來短期滿足感,卻無法提升實力。
例如:不熟位元運算,就專門練習位元運算。這過程初期會痛苦,但這正是大腦「長肌肉」的階段。
重複練習#
做過的題目必須「回鍋」練習:
- 建議頻率:面試前,典型題目至少練習 3 遍
- 善用 LeetCode:利用 Top 100 Liked 或 Frequently Asked 篩選功能
- 專項突破:針對弱點進行標籤篩選(Tag Filtering)
題目做出來並不是結束。務必去 LeetCode 的 Discuss 查看高票答案,比較別人的程式碼與自己的差異,思考最佳化思路。
開發環境設定#
鍵盤設定#
調整系統的鍵盤回應速度能顯著提升 Coding 效率:
- Key Repeat(按鍵重複率):拉到最高
- Delay Until Repeat(重複前延遲):拉到最短
編輯器選擇#
- IDE(推薦):熟練一款,如 PyCharm 或 IntelliJ IDEA
- 輕量編輯器:VS Code(強烈推薦)、Sublime Text
- Vim:必備技能,遠端伺服器 Debug 時常只有 Vim 可用
心態建設#
錯誤心態:將面試官視為「監考老師」,將面試視為「考試」
正確心態:將面試官視為未來的同事
面試本質上是 Teamwork 的過程。你們正在共同探討技術問題,目標是達成共識並解決它。面試官的存在是為了觀察你的溝通模式,並在必要時輔助你。保持開放、合作的心態,能顯著提升錄取機率。
語法要點#
就地交換#
Python 有 x, y = y, x 這種同時求值的多重賦值,能不借助臨時變數就交換兩數。Kotlin 沒有這個語法糖,但可以用 also 達到同樣效果:
// Kotlin:用 also 就地交換,also 先求出右側再賦值
var x = 1
var y = 2
x = y.also { y = x }反轉鏈結串列時,Python 可以一行寫成 prev, curr.next, curr = curr, prev, curr.next;Kotlin 沒有多重賦值,改用一個臨時變數把「下一個節點」先接住即可:
// 反轉鏈結串列的經典寫法(需臨時變數暫存 next)
val nextTemp = curr.next
curr.next = prev
prev = curr
curr = nextTemp關鍵在賦值的求值時機。Python 的
x, y = y, x會先算完右側才一次賦值;而 Kotlin(如 Java/C++)的x = y; y = x;是逐句執行,第一句就把x覆蓋掉了,因此必須靠also或臨時變數保住原值。
Dummy Node(哨兵節點)#
處理鏈結串列頭部操作時,建議使用 Dummy Node:
val dummy = ListNode() // 啞節點作為起點
// ... 操作 ...
return dummy.next // 實際結果這種寫法在邏輯與程式碼可讀性上都更為清晰。