684 字
3 分钟
子集

题目描述#

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

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

解题思路#

  1. 回溯算法框架:采用 DFS 回溯法遍历所有可能组合,从空集开始逐步添加元素
  2. 路径记录:维护当前路径 path,每次递归时记录当前生成的子集
  3. 索引控制:使用 start 参数确保元素按顺序选择,避免重复组合
  4. 递归展开:每层递归决策”是否添加当前元素”并进入下一层
  5. 无剪枝设计:题目要求所有子集,故无需剪枝

关键洞察#

  1. 树形结构特性:子集生成可视为二叉树遍历 - 每个元素有”选/不选”两种状态
  2. 路径复用机制:回溯时通过 pop() 操作复用数组空间,避免额外内存分配
  3. 自动去重原理:索引递增保证元素选择顺序固定,自然避免重复子集
  4. 空集处理:初始空路径 [] 确保包含空集
  5. 即时收集:每次递归入口记录当前路径,确保所有中间状态都被收集

复杂度分析#

指标说明
时间复杂度O(n × 2ⁿ):生成 2ⁿ 个子集,每个子集平均长度 n/2,复制需 O(n) 时间
空间复杂度O(n × 2ⁿ):存储结果所需空间(递归栈 O(n) 可忽略)

代码实现#

递归回溯法

/**
* @param {number[]} nums
* @return {number[][]}
*/
var subsets = function (nums) {
const result = [];
const n = nums.length;
const dfs = (start, item) => {
result.push(item);
for (let i = start; i < n; i++) {
dfs(i + 1, [...item, nums[i]])
}
}
dfs(0, []);
return result;
};

迭代法(空间优化)

/**
* @param {number[]} nums
* @return {number[][]}
*/
const subsets = (nums) => {
let res = [[]];
for (const num of nums) {
const newSets = res.map(set => [...set, num]);
res = [...res, ...newSets];
}
return res;
};

位运算法(常数优化)

/**
* @param {number[]} nums
* @return {number[][]}
*/
const subsets = (nums) => {
const n = nums.length;
const total = 1 << n;
const res = Array(total);
for (let mask = 0; mask < total; mask++) {
const subset = [];
for (let i = 0; i < n; i++) {
if (mask & (1 << i)) subset.push(nums[i]);
}
res[mask] = subset;
}
return res;
};

实际应用场景#

  1. 特征选择:机器学习中枚举特征组合`
  2. 权限组合:系统权限的幂集生成`
  3. 组合优化:穷举搜索问题解空间`
  4. 测试用例生成:软件测试中参数组合覆盖`

相似题目#

子集
https://website-truelovings-projects.vercel.app/posts/algorithms/子集/
作者
欢迎来到StarSky的网站!
发布于
2025-08-10
许可协议
CC BY-NC-SA 4.0