一、核心思路
子集问题属于回溯组合类题型,数组元素互不相同。 关键两点:
- 每走到递归的一层,当前路径
path就是一个合法子集,直接加入结果集(和全排列只收集叶子节点不同); - 使用
start下标控制遍历起点,下一轮只能选当前下标后面的元素,防止出现[1,2]、[2,1]这种重复子集。
二、完整 Java 代码
class Solution {
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
backtrack(nums, 0);
return res;
}
private void backtrack(int[] nums, int start) {
// 步骤1:先把当前path存入结果,一定要新建集合拷贝
res.add(new ArrayList<>(path));
// 步骤2:从start开始向后遍历
for(int i = start; i < nums.length; i++){
// 步骤3:选择当前元素
path.add(nums[i]);
// 步骤4:递归,下一轮只能选i之后的元素 i+1
backtrack(nums, i + 1);
// 步骤5:回溯,撤销选择
path.remove(path.size() - 1);
}
}
}
三、实例演示(nums = [1,2,3],表格模拟)
| 递归 start | path | 是否加入 res | 循环 i | 操作 |
|---|---|---|---|---|
| 0 | [] | ✅ 加入,res: [[]] | i=0 | add 1 → path=[1],递归 start=1 |
| 1 | [1] | ✅ 加入,res: [[],[1]] | i=1 | add 2 → path=[1,2],递归 start=2 |
| 2 | [1,2] | ✅ 加入,res: [[],[1],[1,2]] | i=2 | add3 → path=[1,2,3],递归 start=3 |
| 3 | [1,2,3] | ✅ 加入,res: [[],[1],[1,2],[1,2,3]] | i>=3 循环结束 | return,撤销 3 → path=[1,2] |
| 回到 start=2 | [1,2] | - | i 循环结束 | return,撤销 2 → path=[1] |
| 回到 start=1 | [1] | - | i=2 | add3 → path=[1,3],递归 start=3 |
| 3 | [1,3] | ✅ 加入 | i>=3 循环结束 | return,撤销 3 → path=[1] |
| 回到 start=1 | [1] | - | i 循环结束 | return,撤销 1 → path=[] |
| 回到 start=0 | [] | - | i=1 | add2 → path=[2],递归 start=2 |
| 2 | [2] | ✅ 加入 | i=2 | add3 → path=[2,3],递归 start=3 |
| 3 | [2,3] | ✅ 加入 | 循环结束 | return,撤销 3 → path=[2] |
| 回到 start=2 | [2] | - | i 循环结束 | return,撤销 2 → path=[] |
| 回到 start=0 | [] | - | i=2 | add3 → path=[3],递归 start=3 |
| 3 | [3] | ✅ 加入 | 循环结束 | return,撤销 3 → path=[] |
循环全部结束,最终结果:[[],[1],[1,2],[1,2,3],[1,3],[2],[2,3],[3]]
四、复杂度分析
- 时间复杂度:(O(n* 2^n)),总共有(2^n)个子集,保存每个子集最多拷贝 n 个元素
- 空间复杂度:(O(n)),递归栈最深为 n,path 最多存放 n 个元素
五、易错点总结
res.add(new ArrayList<>(path)),必须 new 新集合,否则 path 后续修改会覆盖 res 里存好的数据;- 递归传参是
i+1,不是start+1,保证只能向后选,避免重复组合; - 子集是每层都收集,不是等循环结束再收集,空集自动被第一层收录,不用手动添加;
- 数组元素互不相同,不需要去重。
六、感悟
子集是回溯组合问题的基础模板,和全排列区分开:
- 子集:start 控制往后选,每层收集 path,不回头;
- 全排列:used 标记是否使用,每次从头遍历,只在 path 长度等于 nums 长度时收集。