Const
子集型回溯:每个元素都可以选或者不选。要生成子集有两种思路,区别在于当前操作是什么:
输入角度:dfs(i)从下标>=i的数字中构造子集
输出角度:子集问题不考虑顺序,为了避免重复我们就规定顺序,让它严格递增
当前操作:每次必须选一个数。枚举下标j>=i的数字,加入path
子问题:从下标>=i的数字中构造子集
下一个子问题:从下标>=j+1的数字中构造子集。
特点
每次递归都可以选“后面所有还没选过的数”,保证了组合的顺序递增(避免重复)。
递归树是“多叉树”结构。
这种写法天然避免了重复子集(比如 [1,2] 和 [2,1] 只会出现 [1,2])。
角度 | 递归树结构 | 适用场景 | 去重方式 |
---|---|---|---|
输入角度 | 二叉树 | 子集、选/不选 | 不需要特判 |
输出角度 | 多叉树 | 组合、顺序无关 | 通过递增下标去重 |
组合型回溯
排列型回溯:进入每层都使用一个新的辅助set用来存储当前层能够候选的数有哪些。
path[i]
本身能够覆盖旧元素,相当于恢复现场// 枚举答案的每一个数的角度 (子集型回溯)
function subsets(nums: number[]): number[][] {
const ans: number[][] = [];
const path: number[] = [];
function dfs(i: number) {
ans.push([...path]);
for (let j = i; j < nums.length; j++) {
path.push(nums[j]);
dfs(j + 1);
path.pop();
}
}
dfs(0);
return ans;
};
// 选或者不选的角度 (子集型回溯)
function subsets2(nums: number[]): number[][] {
const path: number[] = [];
const ans: number[][] = [];
function dfs(i: number) {
if (i === nums.length) {
ans.push([...path]);
return;
}
dfs(i + 1);
path.push(nums[i]);
dfs(i + 1);
path.pop();
}
dfs(0);
return ans;
}
78.子集
给你一个整数数组
nums
,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入:
nums = [1,2,3]
输出:
[[],[1],[2],[3],[1,2],[1,3],[2,3],[1,2,3]]
示例 2:
输入:
nums = [0]
输出:
[[],[0]]
提示:
1 <= nums.length <= 10
-10 <= nums[i] <= 10
nums
中的所有元素 互不相同