LeetCode Hot100(40/100)——78. 子集

文章目录

    • 一、题目概述
    • 二、思维导图
    • 三、解法一:回溯法(Backtracking)
      • [1. 原理解析](#1. 原理解析)
      • [2. 递归流程图](#2. 递归流程图)
      • [3. Java 实现代码](#3. Java 实现代码)
      • [4. 时间与空间复杂度](#4. 时间与空间复杂度)
    • 四、解法二:迭代法(Iterative)
      • [1. 原理解析](#1. 原理解析)
      • [2. 流程图](#2. 流程图)
      • [3. Java 实现代码](#3. Java 实现代码)
      • [4. 时间与空间复杂度](#4. 时间与空间复杂度)
    • [五、解法三:位运算法(Bit Manipulation)](#五、解法三:位运算法(Bit Manipulation))
      • [1. 原理解析](#1. 原理解析)
      • [2. 位运算流程图](#2. 位运算流程图)
      • [3. Java 实现代码](#3. Java 实现代码)
      • [4. 时间与空间复杂度](#4. 时间与空间复杂度)
    • 六、综合对比
    • 七、总结

一、题目概述

题目描述:

给定一个不含重复元素的数组 nums,返回该数组所有可能的子集(幂集)。

示例:

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

说明:

  • 子集的数量为 2^n(n 为数组长度)。
  • 子集顺序不重要。

二、思维导图

子集问题 Subsets
基本思路

  • 枚举所有元素组合 - 每个元素有"选"或"不选"两种状态 解法类别
    回溯法
    迭代法
    位运算枚举法
    分析维度
    时间复杂度
    空间复杂度
    可扩展性

三、解法一:回溯法(Backtracking)

1. 原理解析

思想核心:

我们尝试对数组中的每个元素进行「选」或「不选」,通过回溯递归逐层构建所有可能的子集。

每进入一层递归,都表示我们正在考虑数组中的第 i 个元素是否放入当前子集。

2. 递归流程图



开始
调用 backtrack([], 0)
idx == nums.length?
加入当前子集到结果集
不选择 nums[idx]
backtrack(current, idx+1)
选择 nums[idx]
backtrack(current+[nums[idx]], idx+1)
返回上一层

3. Java 实现代码

java 复制代码
import java.util.*;

public class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        backtrack(res, new ArrayList<>(), nums, 0);
        return res;
    }

    private void backtrack(List<List<Integer>> res, List<Integer> temp, int[] nums, int start) {
        res.add(new ArrayList<>(temp));
        for (int i = start; i < nums.length; i++) {
            temp.add(nums[i]);
            backtrack(res, temp, nums, i + 1);
            temp.remove(temp.size() - 1);
        }
    }
}

4. 时间与空间复杂度

  • 时间复杂度: O(2^n)
    每个元素可选或不选,共 2^n 种组合。
  • 空间复杂度: O(n)(递归栈深度 + 临时子集存储)

四、解法二:迭代法(Iterative)

1. 原理解析

每加入一个新元素 x,都在当前已有子集中附加一份"包含该元素"的新子集。

例如:

复制代码
nums = [1,2,3]

初始: [[]]
加入 1: [[],[1]]
加入 2: [[],[1],[2],[1,2]]
加入 3: [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

2. 流程图

初始结果 [[]]
遍历 nums
对当前结果集 res 中每个 subset 添加 nums[i]
更新结果集 res
完成所有遍历 → 返回 res

3. Java 实现代码

java 复制代码
import java.util.*;

public class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        res.add(new ArrayList<>());
        for (int num : nums) {
            List<List<Integer>> newSubsets = new ArrayList<>();
            for (List<Integer> subset : res) {
                List<Integer> temp = new ArrayList<>(subset);
                temp.add(num);
                newSubsets.add(temp);
            }
            res.addAll(newSubsets);
        }
        return res;
    }
}

4. 时间与空间复杂度

  • 时间复杂度: O(2^n * n)
    每次复制已有子集列表。
  • 空间复杂度: O(2^n * n)(存储全部子集)

五、解法三:位运算法(Bit Manipulation)

1. 原理解析

对每个整数从 02^n - 1,用其二进制位来表示「选中状态」:

  • 二进制第 i 位为 1 表示选中 nums[i]
  • 为 0 表示未选。

例如:
nums = [1, 2, 3]

复制代码
000 -> []
001 -> [3]
010 -> [2]
011 -> [2,3]
100 -> [1]
101 -> [1,3]
110 -> [1,2]
111 -> [1,2,3]

2. 位运算流程图

for mask in (0 .. 2^n-1)
初始化空子集 subset
检查每一位 bit 是否为 1
若为1则加入对应 nums[i]
将 subset 加入结果集 res

3. Java 实现代码

java 复制代码
import java.util.*;

public class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        int n = nums.length;
        List<List<Integer>> res = new ArrayList<>();
        for (int mask = 0; mask < (1 << n); mask++) {
            List<Integer> subset = new ArrayList<>();
            for (int i = 0; i < n; i++) {
                if ((mask & (1 << i)) != 0) {
                    subset.add(nums[i]);
                }
            }
            res.add(subset);
        }
        return res;
    }
}

4. 时间与空间复杂度

  • 时间复杂度: O(2^n * n)
  • 空间复杂度: O(2^n * n)

六、综合对比

解法 思路特点 时间复杂度 空间复杂度 适用场景
回溯法 递归建树结构,自然 O(2^n) O(n) 理解递归思路练习
迭代法 动态生成新子集 O(2^n * n) O(2^n * n) 实现简单,非递归
位运算法 利用二进制状态枚举 O(2^n * n) O(2^n * n) 高效、直观实现

七、总结

  • 子集问题是典型的 组合枚举问题,关键在于理解「选与不选」的二元决策。
  • 回溯法最直观;迭代法实现简洁;位运算法最具数学美感。
  • 掌握这三种思路,将有助于解决其他与「组合 / 幂集 / 枚举」相关的问题,如:
    • 子集和问题(Subset Sum)
    • 子序列生成
    • 组合总和(Combination Sum)
相关推荐
徐小夕1 小时前
pxcharts Ultra V2.3更新:多维表一键导出 PDF,渲染兼容性拉满!
vue.js·算法·github
CoovallyAIHub2 小时前
OpenClaw一脚踩碎传统CV?机器终于不再只是看世界
深度学习·算法·计算机视觉
CoovallyAIHub2 小时前
仅凭单目相机实现3D锥桶定位?UNet-RKNet破解自动驾驶锥桶检测难题
深度学习·算法·计算机视觉
zone77392 小时前
002:RAG 入门-LangChain 读取文本
后端·算法·面试
得物技术3 小时前
得物社区搜推公式融合调参框架-加乘树3.0实战
算法
会员源码网1 天前
使用`mysql_*`废弃函数(PHP7+完全移除,导致代码无法运行)
后端·算法
木心月转码ing1 天前
Hot100-Day10-T438T438找到字符串中所有字母异位词
算法
HelloReader1 天前
Wi-Fi CSI 感知技术用无线信号“看见“室内的人
算法
颜酱1 天前
二叉树分解问题思路解题模式
javascript·后端·算法
qianpeng8971 天前
水声匹配场定位原理及实验
算法