子集-LeetCode
78. 子集 - Subsets
🎯 问题描述(来源于LeetCode)
描述:
给定一个整数数组 nums,数组中的元素互不相同。返回该数组所有可能的子集(幂集)。
解集不能包含重复的子集,可以按任意顺序返回答案。
说明:
- 子集包括空集和原集合本身。
示例:
- 示例 1:
1 | 输入: nums = [1,2,3] |
- 示例 2:
1 | 输入: nums = [0] |
💻 解题思路
思路一:枚举“选哪个”(组合型回溯)
算法步骤
- 获取数组长度
n,初始化结果列表ans和当前路径列表path。 - 定义递归函数
dfs(i),表示当前考虑从下标i开始的元素进行选择:- 首先将当前
path的副本加入ans(对应“不选后面的任何元素”)。 - 然后从
i到n-1遍历元素:将nums[j]加入path,递归调用dfs(j+1),然后弹出path最后一个元素(回溯)。
- 首先将当前
- 从
dfs(0)开始搜索。 - 返回
ans。
思路一:代码实现
1 | from typing import List |
思路二:枚举“选或不选”(分治回溯)
算法步骤
- 获取数组长度
n,初始化结果列表ans和当前路径列表path。 - 定义递归函数
dfs(i),表示处理第i个元素(下标从 0 开始):- 如果
i == n,说明已经对所有元素做出了选择,将当前path的副本加入ans,返回。 - 不选
nums[i]:直接递归调用dfs(i+1)。 - 选
nums[i]:将nums[i]加入path,递归调用dfs(i+1),然后弹出path最后一个元素(回溯)。
- 如果
- 从
dfs(0)开始搜索。 - 返回
ans。
思路二:代码实现
1 | class Solution: |
📊 性能分析
- 时间复杂度:,共 个子集,每个子集复制需要 时间。
- 空间复杂度:,递归栈深度为 ,加上路径数组
path的空间。
思考
- 两种思路都是经典的回溯解法。思路一(枚举选哪个)在递归过程中每次先加入当前子集,然后遍历可选的元素,适合需要按顺序构造子集的场景。思路二(选或不选)更直观地模拟了每个元素是否加入子集的过程,递归树是满二叉树,更容易理解。
- 注意:无论哪种写法,都不能直接
append(path),必须复制一份(path.copy()或path[:]),因为path是可变对象,后续回溯修改会影响已存入的结果。 - 本题也可用二进制枚举法(迭代)实现,但回溯法更具通用性,能轻松扩展到组合、排列等问题。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 笺札!






