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|动态规划]]反而是过度设计。这套心法的一部分是判断力:让投入匹配输入规模与需求。目标是一个正确、清晰、且*足够*快的解——而非可能存在的最聪明的那个。