LeetCode 78. 子集(回溯法)

一、核心思路

子集问题属于回溯组合类题型,数组元素互不相同。 关键两点:

  1. 每走到递归的一层,当前路径path就是一个合法子集,直接加入结果集(和全排列只收集叶子节点不同);
  2. 使用start下标控制遍历起点,下一轮只能选当前下标后面的元素,防止出现[1,2]、[2,1]这种重复子集。

二、完整 Java 代码

csharp 复制代码
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 个元素

五、易错点总结

  1. res.add(new ArrayList<>(path)),必须 new 新集合,否则 path 后续修改会覆盖 res 里存好的数据;
  2. 递归传参是i+1,不是start+1,保证只能向后选,避免重复组合;
  3. 子集是每层都收集,不是等循环结束再收集,空集自动被第一层收录,不用手动添加;
  4. 数组元素互不相同,不需要去重。

六、感悟

子集是回溯组合问题的基础模板,和全排列区分开:

  • 子集:start 控制往后选,每层收集 path,不回头;
  • 全排列:used 标记是否使用,每次从头遍历,只在 path 长度等于 nums 长度时收集。
相关推荐
INGNIGHT1 小时前
151 · 买卖股票的最佳时机 III(序列dp)
数据结构·算法·leetcode
圣保罗的大教堂1 小时前
leetcode 3644. 排序排列 中等
leetcode
Tisfy3 小时前
LeetCode 3644.排序排列:不对则与(脑筋急转弯)
数据结构·算法·leetcode·题解·位运算
文丑颜不良啊4 小时前
LeetCode576~600题目解析
java·leetcode
y1su17 小时前
Leetcode 字符串模板
java·数据结构·算法·leetcode·职场和发展·动态规划
Xin7701 天前
LeetCode 46. 全排列(回溯法)
leetcode
文丑颜不良啊1 天前
LeetCode476~500题目解析
java·leetcode
Tsuki_tl1 天前
【优选算法:双指针】——复写零题解
java·c++·算法·leetcode·数组·双指针·复写零
Cccp.1231 天前
【leetcode】(十四)从暴力递归到动态规划
数据结构·算法·leetcode·动态规划