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)
相关推荐
寻寻觅觅☆8 小时前
东华OJ-基础题-106-大整数相加(C++)
开发语言·c++·算法
偷吃的耗子9 小时前
【CNN算法理解】:三、AlexNet 训练模块(附代码)
深度学习·算法·cnn
化学在逃硬闯CS9 小时前
Leetcode1382. 将二叉搜索树变平衡
数据结构·算法
ceclar12310 小时前
C++使用format
开发语言·c++·算法
Gofarlic_OMS10 小时前
科学计算领域MATLAB许可证管理工具对比推荐
运维·开发语言·算法·matlab·自动化
夏鹏今天学习了吗10 小时前
【LeetCode热题100(100/100)】数据流的中位数
算法·leetcode·职场和发展
忙什么果11 小时前
上位机、下位机、FPGA、算法放在哪层合适?
算法·fpga开发
董董灿是个攻城狮11 小时前
AI 视觉连载4:YUV 的图像表示
算法
ArturiaZ12 小时前
【day24】
c++·算法·图论
大江东去浪淘尽千古风流人物12 小时前
【SLAM】Hydra-Foundations 层次化空间感知:机器人如何像人类一样理解3D环境
深度学习·算法·3d·机器人·概率论·slam