持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第43天,点击查看活动详情.
78. 子集
题目描述
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
示例 2:
输入:nums = [0]
输出:[[],[0]]
解题思路
思路一: 迭代, 利用二进制和位元算
集合的每个元素,都有可以选或不选,用二进制和位运算
一共种组合
实现代码如下:
/**
* @param {number[]} nums
* @return {number[][]}
*/
var subsets = function(nums) {
let len = nums.length, res = [];
for (let i = 0; i < (1 << len); i++) {
let sub = [];
for (let j = 0; j < len; j++)
if (((i >> j) & 1) == 1) sub.push(nums[j]);
res.push(sub);
}
return res;
};
- 时间复杂度: 一共 个状态,每种状态需要 O(n) 的时间来构造子集
- 空间复杂度:O(n)
思路二: 枚举
逐个枚举,空集的幂集只有空集,每增加一个元素,让之前幂集中的每个集合,追加这个元素,就是新增的子集。
循环枚举
实现代码如下:
/**
* @param {number[]} nums
* @return {number[][]}
*/
var subsets = function(nums) {
let res = [[]];
for (let n of nums) {
let len = res.length;
for (let i = 0; i < len; i++) {
let newSub = [...res[i]];
newSub.push(n);
res.push(newSub);
}
}
return res;
};
思路三: 回溯
- 排序(升序或者降序都可以),排序是剪枝的前提
nums.sort((a, b) => a - b); - 考虑重复元素,剪枝去重
if (j > i && nums[j] == nums[j - 1]) continue;
实现代码如下:
/**
* @param {number[]} nums
* @return {number[][]}
*/
var subsets = function(nums) {
let len = nums.length,
res = [[]];
nums.sort((a, b) => a - b)
backtrack(nums, len, 0, [], res)
return res;
};
function backtrack(nums, len, i, sub, res) {
for (let j = i; j < len; j++) {
// 剪枝去重
if (j > i && nums[j] == nums[j - 1]) continue;
sub.push(nums[j]);
res.push([...sub]);
backtrack(nums, len, j + 1, sub, res);
// 撤销选择
sub.pop();
}
}
- 时间复杂度为: ,其中n为数组nums的长度