LeetCode 78. 子集

题目描述

子集

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

示例:

输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]

解法一(递归)

观察到输出集合从空集开始,依次遍历nums内的数值,每遍历一个数值,就往输出集合中加入原来的所有子集中添加当前遍历到的数值。例如,遍历到nums内的数值2,输出集合原来的子集有[]、[1],需要往集合中添加[2]、[1,2]。使用递归实现这一过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> ans;
ans.push_back(vector<int> {});
subsetsCore(nums, ans, 1);
return ans;
}

void subsetsCore(vector<int>& nums, vector<vector<int>>& ans, int level) {
if (level > nums.size())
return;
int size = ans.size();
for (int i = 0; i < size; i++) {
vector<int> tmp = ans[i];
tmp.push_back(nums[level - 1]);
ans.push_back(tmp);
}
subsetsCore(nums, ans, level + 1);
}
};

解法二(回溯)

输出集合从空集开始,回溯先从nums的第一个数值1开始,依次得到[1]、[1,2]、[1,2,3]、[1,3],然后再从nums的第二个、第三个数值开始,直到回溯结束。

回溯算法模版
1
2
3
4
5
6
7
8
9
result = []
def backtrack(路径, 选择列表):
if 满足结束条件:
result.add(路径)
return
for 选择 in 选择列表:
做选择
backtrack(路径, 选择列表)
撤销选择
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
backtrace(nums, 0);
return ans;
}

void backtrace(vector<int>& nums, int pos) {
ans.push_back(path);
for (int i = pos; i < nums.size(); i++) {
path.push_back(nums[i]);
backtrace(nums, i + 1);
path.pop_back();
}
}

private:
vector<vector<int>> ans;
vector<int> path {};
};

解法三(位运算)

子集的所有情况其实可以用n位的二进制(n 为 nums.size()),例如上面的例子可以用3位二进制表示所有情况,000、001、010、011、100、101、110、111。不过如果nums的size大于一定数值时会溢出(例如int类型不能超过32位),可以使用bitset。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> ans;
int N = 1 << nums.size();
for (int i = 0; i < N; i++) {
vector<int> tmp;
for (int j = 0; j < nums.size(); j++) {
if (i & (1 << j)) {
tmp.push_back(nums[j]);
}
}
ans.push_back(tmp);
}
return ans;
}
};