#78 子集
- 状态与选择
- 已选路径与下一可选起始索引;后续索引递增。
- 记录与终止
- 每个搜索节点都是一个合法子集,都需要记录独立快照。
- 合法性与剪枝
- 不施加额外长度限制,索引递增避免顺序重复。
- 恢复与不变量
- 子调用结束撤销当前选择,父状态恢复到调用前。
Unit 00 · 回溯与状态空间搜索基础
每个递归调用代表一个状态;每条边代表一次选择;约束决定哪些分支不值得继续;返回父状态前必须撤销本层对共享状态造成的修改。
通过最小实验“从候选 `[1,2,3]` 中构造长度不超过2的路径”,拆解回溯中的六个独立问题:
足以描述“当前搜索走到哪里、已经做了什么选择、哪些候选仍可用”的最小信息集合。
节点是状态,边是选择,根是初始状态,叶子可能是答案、失败状态或被剪枝状态。
依据约束证明某个分支不可能产生目标答案,因此不再展开其后代。
把共享状态恢复到进入当前递归层之前的样子,使兄弟分支从正确父状态开始。
回溯与搜索:在选择、探索与撤销中,构建完整的状态空间。
这里的梯度描述认知任务的递进,不等同于题库的难度标签。点击题目可进入对应单元。
用同一组问题定位每个单元,再观察迁移题改变了哪个条件。进入写代码环节后,学习地图会收起,留出独立思考的空间。
path保存已经做出的选择;start限制下一层只能选择更靠后的索引。
path 恰好表示根到当前搜索节点的选择;其中元素索引严格递增。
子调用结束后,执行 path.pop(),使父调用继续下一个兄弟选择时看到原来的父状态。
记录答案表示当前状态满足输出要求;终止表示不需要继续深入;剪枝表示依据约束证明后续不可能产生目标答案。三者可能同时发生,也可能彼此独立。
路径、访问标记和约束集合通常被多个递归调用共享。修改之后若不恢复,或记录答案时不复制,会让兄弟分支和历史答案同时被污染。
子分支留下的元素进入兄弟分支,导致路径不再对应当前树节点。
所有答案卡片引用同一个列表对象,后续修改会同步改变历史答案。
正常完成、失败返回、找到一个解后提前结束,与约束剪枝的语义不同。
可以为每个子调用构造新状态,也可以共享一份状态并显式撤销。前者更直观但复制成本更高;后者更高效,但必须严格维护恢复不变量。
删除“路径长度最多为2”的限制,使搜索遍历所有递增索引路径。关键判断:子集题中每个搜索节点都是一个合法答案,所以进入调用后立即复制path。
回溯的核心不是递归语法,而是状态契约:进入调用时状态代表当前树节点;选择后进入子状态;返回父层前恢复共享状态;记录答案时复制快照;剪枝必须有明确约束依据。