78. 子集

46. 全排列

中等

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

示例 1:

复制代码
输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

示例 2:

复制代码
输入:nums = [0]
输出:[[],[0]]

提示:

  • 1 <= nums.length <= 10
  • -10 <= nums[i] <= 10
  • nums 中的所有元素 互不相同

📝 核心笔记:子集 (Subsets - 选或不选法)

1. 核心思想 (一句话总结)

"每个数字都有两种命运:要么进来,要么滚蛋。"

我们遍历数组中的每一个数字,对于 numsi,我们只有两个分支:

  1. 不选它:直接跳过,处理下一个。
  2. 选它 :把它装进 path,处理下一个,回来后再把它拿出来(回溯)。

💡 图像记忆 (二叉决策树):

  • 这就像走到岔路口。
  • 左拐:不带这个行李。
  • 右拐:带上这个行李。
  • 一直走到路的尽头 (i == n),也就是对所有行李都做完了决定,这时候拍张照(加入结果集)。
2. 算法流程 (三步走)
  1. Base Case (触底)
    • i == nums.length 时,说明对所有数字都做过决定了。
    • 此时 path 里存的就是一种完整的子集方案,复制 并加入 ans
  1. 分支一:不选 (Exclude)
    • 啥都不做,直接 dfs(i + 1)
  1. 分支二:选 (Include)
    • path.add(nums[i])
    • dfs(i + 1)
    • 回溯path.removeLast() (恢复现场,为了回退到上一层时不影响其他分支)。
🔍 代码回忆清单 (带注释版)
复制代码
// 题目:LC 78. Subsets
class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> ans = new ArrayList<>();
        List<Integer> path = new ArrayList<>();
        dfs(0, nums, path, ans);
        return ans;
    }

    // i: 当前讨论到了第几个数
    private void dfs(int i, int[] nums, List<Integer> path, List<List<Integer>> ans) {
        // 1. Base Case: 到了数组末尾 (叶子节点)
        // 这里的逻辑是:必须对每个数都表态后,才结算
        if (i == nums.length) { 
            ans.add(new ArrayList<>(path)); // 📸 拍照留念 (深拷贝)
            return;
        }

        // 2. 决策 A: 不选当前数
        // 这种写法通常先写"不选",因为不需要操作 path,代码更清爽
        dfs(i + 1, nums, path, ans); 

        // 3. 决策 B: 选当前数
        path.add(nums[i]);        // 做选择
        dfs(i + 1, nums, path, ans); // 递归
        path.removeLast();        // 撤销选择 (回溯)
    }
}
⚡ 快速复习 CheckList (易错点)
  • 什么时候 add****到 ans**?**
    • 本解法 (选/不选) :只有在 i == n (叶子节点) 时才添加。因为中间过程的 path 只是半成品状态(虽然也是子集,但在这个逻辑里我们约定只在终点结算)。
    • 对比 :如果是 for 循环枚举 的写法,是进入 DFS 每一步都 add
  • 先"选"还是先"不选"?
    • 都行!通常先写"不选",因为不用写 add/remove,代码看着少一行,逻辑也不乱。
  • 复杂度是多少?
    • 时间:。每个数 2 种选择,共有 个子集,复制每个子集平均 。
    • 空间:。递归栈深度为 。
🖼️ 数字演练

输入 nums = [1, 2]

  1. Start dfs(0): 面对 1。
  2. Branch "No 1":
    • dfs(1): 面对 2。
    • Branch "No 2" : dfs(2) -> 到底 -> Add []
    • Branch "Yes 2" : path=[2] -> dfs(2) -> 到底 -> Add [2] -> 回溯 (pop 2)。
  1. Branch "Yes 1":
    • path=[1] -> dfs(1): 面对 2。
    • Branch "No 2" : dfs(2) -> 到底 -> Add [1]
    • Branch "Yes 2" : path=[1, 2] -> dfs(2) -> 到底 -> Add [1, 2] -> 回溯 (pop 2)。
    • 回溯 (pop 1)。

(最终结果: []****, [2]****, [1]****, [1, 2]****)

相关推荐
杨运交34 分钟前
[071][验证码模块]基于Spring拦截器的验证码认证设计思想
java·后端·spring
Geek-Chow1 小时前
CountDownLatch in Java
java
漂流瓶jz1 小时前
UVA-1442 洞穴 题解答案代码 算法竞赛入门经典第二版
c++·算法·题解·aoapc·算法竞赛入门经典·uva
SL_staff1 小时前
从RBAC到场景化授权:《无忧·企业文档》三级权限模型的技术实践解析
java·开源·产品
传奇开心果编程2 小时前
【springboot基础语法学与练】第 1 课:从零开始
java·spring boot·后端·学习
SL_staff2 小时前
财务系统慎用低代码?从数据模型闭环看合规落地的技术实践
java·低代码·全栈
叠层归一研究院2 小时前
基于极限自指的叠层归一宇宙结构理论——无元外部封闭系统的内生区分模型
人工智能·经验分享·算法·agi
滕州市燕猫虎计算机科技工作室个体工商户2 小时前
IDEA:Command line is too long
java·ide·intellij-idea
步行cgn3 小时前
Spring p 命名空间注入详解
java·前端·spring
YatHinLay3 小时前
MyBatis 流式查询实战:ResultHandler 处理海量数据
java