子集问题实战总结:DFS 回溯法
一、问题定义
给定一个元素互异的集合,找出所有可能的子集(包括空集)。例如:
- 输入:$[1,2,3]$
- 输出:$[], [1], [2], [3], [1,2], [1,3], [2,3], [1,2,3]$
二、DFS回溯法核心思想
- 状态树遍历:每个元素有"选"与"不选"两种状态
- 回溯框架:
def backtrack(start, path): 添加当前路径到结果集 for i in range(start, n): 选择当前元素 backtrack(i+1, path) # 递归 撤销选择(回溯) - 避免重复:通过
start参数控制遍历起点
三、解题步骤
- 初始化:
- 结果集
res = [] - 当前路径
path = []
- 结果集
- DFS函数设计:
def dfs(start, path): res.append(path[:]) # 深拷贝当前路径 for i in range(start, len(nums)): path.append(nums[i]) # 选择当前元素 dfs(i+1, path) # 递归下一层 path.pop() # 回溯撤销 - 递归终止:当
start超出数组范围时自动结束 - 复杂度分析:
- 时间复杂度:$O(2^n)$(每个元素两种选择)
- 空间复杂度:$O(n)$(递归栈深度)
四、完整代码实现
def subsets(nums):
res = []
def dfs(start, path):
res.append(path[:]) # 关键:复制当前路径
for i in range(start, len(nums)):
path.append(nums[i])
dfs(i+1, path) # 从下一个位置开始
path.pop() # 回溯
dfs(0, [])
return res
# 测试用例
print(subsets([1,2,3]))
# 输出:[[],[1],[1,2],[1,2,3],[1,3],[2],[2,3],[3]]
五、关键技巧
- 深拷贝陷阱:
res.append(path[:])避免后续修改影响结果 - 剪枝优化:当元素可能重复时需排序并跳过相同元素
if i > start and nums[i] == nums[i-1]: continue - 迭代替代方案:可用位运算($0$ 到 $2^n-1$ 的二进制表示)
六、变式问题
- 重复元素:先排序,在递归中跳过重复值
- 组合求和:添加目标和判断条件
- 排列问题:移除
start参数,每次从 $0$ 开始遍历
总结:DFS回溯法是解决子集问题的通用范式,核心在于掌握"选择-递归-撤销"的三步操作框架,注意状态管理和剪枝优化。


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



