基本步骤
这种回溯类型的题目老是莫名其妙的可以做出来,但是不清楚具体的逻辑
下次做回溯类型的题目可以画出这个决策树
回溯 = 在这棵树上 DFS 深度优先遍历,走所有可能的路径
(sum=0, path=[])
/ \
+2 / \ +3
/ \
(sum=2, [2]) (sum=3, [3])
/ \ |
+2 / \ +3 +2
/ \ |
(sum=4, [2,2]) (sum=5, [2,3]) (sum=5, [3,2])
|
+2? → sum=6 >5 → 剪枝
面对回溯题,我应该如何思考
-
想清楚每一步要做什么选择,这决定了 for 循环的内容
- 全排列:当前要填第
i个位置,我能选“还没用过的数” - 组合总和:当前要选一个数加入
path,我能选“从start开始的数”(避免重复组合)
- 全排列:当前要填第
-
设计递归的函数,我需要记住哪些信息才能继续走?dfs(…) 的参数。
path:当前走了哪些路(当前组合)sum/target:当前状态(如和、深度等)start:从哪开始选(避免重复)used[]:哪些元素已用(排列问题)
-
写“终止条件” —— 什么时候停下来?
- 成功,
sum == target→ans.add(new ArrayList<>(path)) - 失败,
sum > target→ 直接返回(剪枝)
- 成功,
-
写“循环 + 选择 + 递归 + 撤销”
-
for (每个可选的选项) { if (剪枝) continue; // 1. 做选择(记录状态) path.add(x); // 2. 递归:走进下一层 dfs(新状态); // 3. 撤销选择(恢复现场) path.remove(path.size() - 1); }
-
如何看懂嵌套的函数调用
大多数人的误区是:
我要跟着 dfs 一层层进去,看它怎么执行……”
结果越跟越晕
正确做法是:
假设 dfs 能搞定“从当前状态出发的所有解” ,你只关心“我做了一个选择后,剩下的交给 dfs 去处理”
你只相信 dfs 会把“以当前 path 为前缀的所有解”都找出来,只要处理好当前的选择问题,剩下的就不要考虑了。
同时最好画一下决策树,这样能够更有利于判断。
3874

被折叠的 条评论
为什么被折叠?



