78. 子集
代码
ts
function subsets(nums: number[]): number[][] {
const res = [];
const len = nums.length;
const path = [];
function dfs(i) {
res.push([...path]);
for (let j = i; j < len; j++) {
path.push(nums[j])
dfs(j + 1);
path.pop()
}
}
dfs(0);
return res;
};思路
使用回溯枚举所有可能的子集:
- 定义结果数组
res保存所有子集 - 使用
path记录当前已经选择的元素 - 递归函数
dfs(i)表示从下标i开始选择元素 - 每进入一层递归,都先把当前
path加入结果,因为它本身就是一个合法子集 - 然后从
i开始向后枚举,每次选择一个元素加入path - 递归处理后续位置,最后再撤销当前选择
示例
对于 nums = [1,2,3]:
- 一开始
path = [],先加入结果,表示空集 - 选择
1后,得到[1] - 在
[1]的基础上继续选择2,得到[1,2] - 再选择
3,得到[1,2,3] - 回溯后继续尝试
[1,3]、[2]、[2,3]、[3]
关键点
每个状态下的
path都是一个合法子集,所以递归一开始就要收集答案通过
dfs(j + 1)保证后续只会选择当前位置之后的元素,避免重复回溯的核心仍然是“选择、递归、撤销选择”
时间复杂度
,共有 个子集,每次加入结果需要拷贝当前路径 空间复杂度
,主要是递归栈和路径数组开销,不计结果数组