第7章|回溯与搜索

Unit 04 · LeetCode #90 · 优化版

← Unit 03
90
Subsets IISort · Same-level Dedup

重复值可以进入同一条路径,
但不能在同一层复制整棵子树。

排序把相同值放到相邻位置。回溯扫描当前候选窗口时,只跳过同一父状态下的后续相同值;进入下一层后,后一个副本仍可被选择,因此 [2,2] 不会丢失。

LeetCode #90 题目模型输入可重复,输出子集必须唯一

题目要求

给定一个可能包含重复元素的整数数组 nums,返回所有可能的子集,结果中不能包含重复子集。

class Solution: def subsetsWithDup(self, nums: List[int]) -> List[List[int]]: pass
  • 每个搜索节点仍代表一个合法子集;
  • 输入值可以重复,但输出路径值序列必须唯一;
  • 本页使用 [1,2,2] 观察同层与同路径的区别。

核心条件

nums.sort()

if index > start and nums[index] == nums[index - 1]:
continue

index > start 把重复判断限定在当前递归层;当 index == start 时,即使值与前一个相同,也允许形成 [2,2]。

查看 LeetCode 原题
Canonical Sort

先排序,使相同值相邻,局部条件才能识别等价候选。

Same-level Duplicate

同一父状态下,相同值作为分支起点会生成等价后代,只保留第一个。

Same-path Duplicate

不同深度可以连续选择相同值,形成合法子集 [2,2]。

index > start

表示当前候选不是本层第一个位置,是去重范围的关键边界。

1. 等价分支
2. 排序相邻
3. 同层跳过
4. 错误条件实验
5. 渐隐脚手架+逐行联动
6. 迁移 #47
环节 1/6
nums=[1,2₁,2₂]|子集状态空间观察重复分支

状态空间树

当前递归层

状态变量

path[]
start0
index—
判断结果尚未判断
skip = index > start and nums[index] == nums[index - 1]

本层候选

已确认的唯一子集

当前状态解释

子集 II 搜索契约

三个位置条件

搜索事件记录

当前 path[]
当前 start0
当前 index—
是否同层重复未判断
记录时机进入每个调用后记录
下一层index + 1

唯一性不变量

完整性不变量

唯一子集1 / 6
当前深度0
本层候选数3
已跳过分支0
当前规则待判断
错误条件会在“重复答案”和“漏掉 [2,2]”之间摆动

切换三个实验,观察被破坏的范围。

完整演示→ 功能提示槽位→ 关键代码补全→ 独立完成 脚手架只减少提示,不限制环节切换。
关键槽位补全先选槽位 → 再选代码
待填槽位(只提示作用)
待选代码标签
实时拼接后的代码骨架0 / 6

    
当前认知焦点
当前递归栈
代码 × 状态逐行联动每一步只更新真实发生的状态
图状态
状态依赖问题

到达关键状态时,这里会出现问题;它不会阻塞下一步或自动播放。

独立完成关闭提示后,从算法契约自行写出完整代码

第7章 Unit 04 Skill 卡

子集 II 的去重对象不是“重复值”,而是同一递归层中的等价选择。排序建立相邻结构,index > start 限定同层范围;进入下一层后,重复副本仍可继续使用。

主状态path + start
预处理nums.sort()
同层去重index>start 且值等于前值
同路径重复index==start 时允许继续选择
记录时机每个搜索节点立即记录
#47 增量用 not used[index-1] 表达同层
复杂度最坏 O(n·2ⁿ)
常见错误index>0、不排序、全局禁值
下一题#39 组合总和
本环节已完成完成状态已记录;所有环节始终可以自由切换。