JOVANA
Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

解題心法

懂得資料結構與演算法只是成功的一半;另一半是面對陌生問題時那套冷靜、可複用的*方法*。本篇給你一個七步流程——釐清、舉例、暴力解、找瓶頸、選模式、分析大 O、測邊界——並展示本階梯裡的每個模式如何對應到它上面。

一個七步方法

初學者和老練的解題者之間最大的差別,不是天生的聰明——而是在答案並不顯然時,有一套可以退守的流程。一張白紙令人僵住;一份清單不會。下面是一個你可以按順序、不慌不忙地套用到幾乎任何演算法問題上的方法。

  1. 釐清。用自己的話複述問題。輸入和輸出究竟是什麼?規模與取值範圍如何?會有重複、負數、空輸入嗎?動手寫程式碼之前先問清楚。
  2. 手算小例子。取一個極小的輸入,在紙上解出來。親手求解這個動作,幾乎總能揭示出真正演算法的結構。
  3. 先寫暴力解。找出任何一個正確的解法,無論多慢。一個能跑的 O(n^2),勝過一個有 bug 的「聰明」想法——而且它給了你一個可以去優化的起點。
  4. 找瓶頸。分析暴力解的大 O,問自己:哪一步是昂貴的那一步?正是這一步,會帶來你全部的改進空間。
  5. 選一個能攻克瓶頸的模式或結構。反覆查找?用雜湊表。重複的子問題?用動態規劃。在有序陣列上掃描?用雙指標。(對應見下。)
  6. 分析新的大 O。這個模式真的降低了代價嗎?把時間與空間複雜度像前面台階教你的那樣大聲說出來。若沒有改進,換一個模式再試。
  7. 測邊界情形。空輸入、單個元素、全部相等、最大規模、負數。讓你的程式碼逐一走過它們。絕大多數 bug 就藏在這裡。

把問題對應到模式

第 5 步——選模式——看起來是難處,但它主要靠「認出來」。你在這條階梯上學過的每個模式,都有一句標誌性的提示語。當你在題目裡聽到那句話,對應的模式就亮起來。訓練自己把題目的語言翻譯成模式的語言。

Problem says ...                          Reach for ...
-------------------------------------     ------------------------------
"have I seen this value before?"          hash table / set (avg O(1))
"find a value in sorted data"             binary search (O(log n))
"smallest / largest so far, repeatedly"   heap / priority queue
"all paths / all combinations"            backtracking (with pruning)
"overlapping subproblems"                 dynamic programming
"contiguous window over an array"         sliding window (O(n))
"pair in a sorted array"                  two pointers (O(n))
"shortest path / levels in a graph"       BFS
"reachability / cycles / components"      DFS or union-find
"split, solve halves, combine"            divide and conquer
一張「提示語→模式」速查表。起主要作用的是認出來,而非憑空發明。

一個完整例子把這些串起來。「給定一個陣列,是否存在兩個數之和等於目標值?」暴力解:檢查每一對,O(n^2)。瓶頸:對每個數,你都重新在整個陣列裡找它的補數。「我之前見過這個值嗎?」這句話亮了——於是用[[hash-table|雜湊表]]:邊掃描邊存下每個數,對當前這個數,檢查 (目標 − 當前) 是否已被存過。這把內層搜索變成平均 O(1),於是整體是 O(n)。一個模式,施於瓶頸之上,就把 n^2 壓成了 n。

bool twoSum(const vector<int>& a, int target) {
    unordered_set<int> seen;
    for (int x : a) {
        if (seen.count(target - x)) return true;  // complement already seen
        seen.insert(x);
    }
    return false;
}
// Brute force was O(n^2). With the hash set, this is O(n) time, O(n) space.
兩數之和的解:瓶頸(重複搜索)遇上了對的模式(雜湊),O(n^2) 就變成了 O(n)。

保持冷靜與誠實

還有兩個習慣,區分了不斷成長的人與原地踏步的人。第一是把你的思路大聲講出來——邊做邊說出你的假設、你的暴力解、你的瓶頸。它讓你保持誠實;在面試中,也讓別人能幫到你。第二是願意說「我的第一個想法是 O(n^2),讓我看看能不能做得更好」,而不是假裝那個快速答案是憑空、完整地降臨的。

並非每個問題都需要花俏的模式。有時暴力解*就是*答案,因為 n 很小,去搬出[[dynamic-programming|動態規劃]]反而是過度設計。這套心法的一部分是判斷力:讓投入匹配輸入規模與需求。目標是一個正確、清晰、且*足夠*快的解——而非可能存在的最聰明的那個。