刷题模式总结:常见题型与解题模板
这一课你将能做到
- 看到题目快速归类到「两数之和」「Top K」「区间合并」等模式
- 直接套用模板代码,减少思考成本
- 根据数据范围预判算法复杂度是否可行
为什么要学
刷题不是拼智商,是拼模式识别。95% 的中等题都是经典模式的变种。掌握 20 个核心模式,胜过做 200 道零散题目。
人话:就像学数学公式,遇到面积题就用 S=ab,遇到速度题就用 v=s/t。刷题也有公式。
先建立图像
题型 → 模式 → 模板 → 代码
两数之和 → 哈希表/双指针
Top K → 堆/快排选择
区间合并 → 排序+贪心
树遍历 → DFS/BFS
最短路 → Dijkstra/BFS
跟着做
步骤 1:哈希表模式
import java.util.*;
public class Main {
// 两数之和(无序数组)
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int need = target - nums[i];
if (map.containsKey(need)) {
return new int[]{map.get(need), i};
}
map.put(nums[i], i);
}
return new int[]{-1, -1};
}
// 无重复最长子串
public int lengthOfLongestSubstring(String s) {
Map<Character, Integer> last = new HashMap<>();
int left = 0, max = 0;
for (int right = 0; right < s.length(); right++) {
char c = s.charAt(right);
if (last.containsKey(c) && last.get(c) >= left) {
left = last.get(c) + 1;
}
last.put(c, right);
max = Math.max(max, right - left + 1);
}
return max;
}
public static void main(String[] args) {
Main m = new Main();
int[] res = m.twoSum(new int[]{2,7,11,15}, 9);
System.out.println(res[0] + ", " + res[1]);
}
}
适用:查找、去重、统计频率。复杂度 O(n)。
步骤 2:Top K 模式
import java.util.*;
public class Main {
// 第 K 大元素
public int findKthLargest(int[] nums, int k) {
PriorityQueue<Integer> heap = new PriorityQueue<>(); // 小顶堆
for (int num : nums) {
heap.offer(num);
if (heap.size() > k) heap.poll();
}
return heap.peek();
}
// 前 K 高频
public List<Integer> topKFrequent(int[] nums, int k) {
Map<Integer, Integer> count = new HashMap<>();
for (int num : nums) count.put(num, count.getOrDefault(num, 0) + 1);
PriorityQueue<Map.Entry<Integer, Integer>> heap = new PriorityQueue<>(
Comparator.comparingInt(Map.Entry::getValue)
);
for (Map.Entry<Integer, Integer> e : count.entrySet()) {
heap.offer(e);
if (heap.size() > k) heap.poll();
}
List<Integer> res = new ArrayList<>();
while (!heap.isEmpty()) res.add(heap.poll().getKey());
return res;
}
public static void main(String[] args) {
Main m = new Main();
System.out.println(m.findKthLargest(new int[]{3,2,1,5,6,4}, 2)); // 5
}
}
适用:前 K 大/小、中位数。复杂度 O(n log k)。
步骤 3:区间合并模式
import java.util.*;
public class Main {
public int[][] merge(int[][] intervals) {
Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
List<int[]> res = new ArrayList<>();
for (int[] interval : intervals) {
if (res.isEmpty() || res.get(res.size() - 1)[1] < interval[0]) {
res.add(interval);
} else {
res.get(res.size() - 1)[1] = Math.max(res.get(res.size() - 1)[1], interval[1]);
}
}
return res.toArray(new int[0][]);
}
public static void main(String[] args) {
Main m = new Main();
int[][] res = m.merge(new int[][]{{1,3},{2,6},{8,10},{15,18}});
for (int[] r : res) System.out.println(Arrays.toString(r)); // [1,6] [8,10] [15,18]
}
}
适用:区间重叠、会议室安排、插入区间。复杂度 O(n log n)。
步骤 4:二叉树遍历模板
import java.util.*;
public class Main {
// 前序遍历(根左右)
public List<Integer> preorder(TreeNode root) {
List<Integer> res = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
while (root != null || !stack.isEmpty()) {
while (root != null) {
res.add(root.val);
stack.push(root);
root = root.left;
}
root = stack.pop().right;
}
return res;
}
// 层序遍历(BFS)
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if (root == null) return res;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
int size = queue.size();
List<Integer> level = new ArrayList<>();
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
level.add(node.val);
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
res.add(level);
}
return res;
}
public static void main(String[] args) {}
static class TreeNode {
int val;
TreeNode left, right;
TreeNode(int x) { val = x; }
}
}
步骤 5:数据范围预判
n ≤ 20 → 枚举/回溯/状态压缩
n ≤ 100 → O(n³) 动态规划
n ≤ 1000 → O(n²) 双循环/DP
n ≤ 10^5 → O(n log n) 排序/二分/堆
n ≤ 10^6 → O(n) 线性扫描
n ≤ 10^9 → O(log n) 数学/二分
讲清楚要点
- 模式识别优先:看到「两个数的和」→ 哈希表;看到「前 K」→ 堆;看到「区间」→ 排序+贪心。
- 模板记忆:记住核心代码结构,细节现场推导。
- 复杂度预判:根据 n 的范围直接锁定可用算法,排除不可能的选项。
- 边界处理:空输入、单个元素、最大最小值是 WA 的三大来源。
你可能会懵的地方
-
怎么知道这道题是什么模式? 看关键词。"和为 target"→哈希表;"前 K"→堆;"区间"→排序+贪心;"最短路径"→BFS/Dijkstra。
-
模板背不下来怎么办? 不用背全部,记住核心结构。哈希表就是「遍历查补」;堆就是「入堆超量弹」;滑动窗口就是「右扩左缩」。
-
一题多解怎么选? 看数据范围。n≤10^3 选 O(n²),n≤10^5 选 O(n log n)。空间换时间是常规策略。
-
为什么写对了还 TLE? Java 常数大。检查 IO(BufferedReader),检查不必要的对象创建(StringBuilder 攒输出)。
-
怎么提高模式识别速度? 按模式分类刷题,不要按顺序刷。每个模式集中做 10 道题,就能一眼认出。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
| 用数组扫描代替哈希表 | O(n²) TLE | 空间换时间 |
| Top K 直接排序 | O(n log n) 超时 | 堆优化到 O(n log k) |
| 区间不排序直接合并 | 结果错误 | 先按左端点排序 |
| DFS 不处理边界 | 栈溢出或结果错误 | 加 null 判断和剪枝 |
| 无视数据范围选算法 | 超时或内存超限 | 先算 n 的范围再选算法 |
卡点急救
- 不知道什么模式:列举题目关键词,对照上面的模式表。还是不知道就试暴力,再优化。
- 模板套用出错:对比标准模板,逐行检查变量名和边界条件。
- 常数因子导致 TLE:用
BufferedReader读入,StringBuilder输出,避免频繁 new 对象。
面试常问
-
刷题最有效的方法是什么? 按模式分类刷,每类 10 道。第一遍理解,第二遍默写,第三遍变形。
-
如何判断算法复杂度是否可行? 代入 n 的值:10^5 时 O(n²)=10^10 必挂,O(n log n)=10^6 可以。
-
面试官要求优化怎么办? 先给暴力解,说明复杂度;再分析瓶颈,提出优化方向;最后写最优解。
自测三问
- 看到「两个数的和」应该用什么模式?
- n=10^5 时哪些复杂度是可行的?
- 区间合并模式的核心步骤是什么?
小结一句话
刷题不是记忆,是模式识别;掌握 20 个核心模式,胜过做 200 道零散题目。
延伸
- LeetCode 官方题解
- 下一课:实战演练——综合题目拆解。