版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/princexiexiaofeng/article/details/79645760
题目描述:
Given a set of distinct integers, nums, return all possible subsets (the power set).
Note: The solution set must not contain duplicate subsets.
For example, if nums = [1,2,3]
, a solution is:
[ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]
分析:
题意:给定一个整型数组集合,返回它的所有不重复子集。
思路:这道题考察运用位运算来进行集合运算。我们采用规则⑨可以解决本题。
在程序中表示集合的方法有很多种,当元素个数比较少时,用二进制来表示比较方便。集合{0, 1, 2, ... , n - 1}的子集S可以用如下方式编码:
① 空集∅:0
② 只含有第i个元素的集合{i}:1 << i
③ 含有全部你个元素的集合{0, 1, 2, ... , n - 1}:(1 << n) - 1
④ 判断第i个元素是否属于集合S:if ( S >> i & 1)
⑤ 向集合中加入第i个元素(S∪{i}):S | 1 << i
⑥ 从集合中去除第i个元素(S \ {i}):S & ~(1 << i)
⑦ 集合S和T的并集:S | T
⑧ 集合S和T的交集:S & T
⑨ 枚举某个集合sup的子集:这里sup是一个二进制码,其本身也是某个集合的子集。例如给定了01101101这样的集合,要将01100000或者00101101等子集枚举出来。此时sub + 1并不一定时sup的子集。而(sub + 1) & sup虽然是sup的子集,可是很有可能依旧是sub,没有改变。
因此我们反过来计算,从sup开始每次减1直到0为止。由于sub - 1并不一定是sup的子集,所以我们把它与sup进行按位与&,可以将sup所有的子集按照降序列举出来。
int sub = sup;
do {
sub = (sub - 1) & sup;
} while(sub != sup);
⑩ 枚举集合{0, 1, 2, ... , n - 1}所包含的所有大小为k的子集的方法:
int comb = (1 << k) - 1;
while (comb < 1 << n) {
int x = comb & -comb, y = comb + x;
comb = ((comb & ~y) / x >> 1) | y;
}
代码:
#include <bits/stdc++.h>
using namespace std;
/*
int sub = sup;
do{
sub = (sub - 1) & sup;
}while(sub != sup);
*/
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> ans;
int n = nums.size();
if (n == 0) {
return ans;
}
int sup = 0;
int min_e = INT_MAX, max_e = INT_MIN;
for (int i = 0; i <= n - 1; i++) {
sup += (1 << nums[i]);
min_e = min(min_e, nums[i]);
max_e = max(max_e, nums[i]);
}
int sub = sup;
do {
vector<int> res;
for (int i = min_e; i <= max_e; i++) {
if (sub & (1 << i)) {
res.push_back(i);
}
}
ans.push_back(res);
sub = (sub - 1) & sup;
} while(sub != sup);
return ans;
}
};