vector<vector<int> > subsetsWithDup(vector<int> &S) {
sort(S.begin(), S.end()); // 必须排序
set<vector<int> > result;
const size_t n = S.size();
vector<int> v;
for (size_t i = 0; i < 1U << n; ++i) {
for (size_t j = 0; j < n; ++j) {
if (i & 1 << j)
v.push_back(S[j]);
}
result.insert(v);
v.clear();
}
vector<vector<int> > real_result;
copy(result.begin(), result.end(), back_inserter(real_result));
return real_result;
}
2. 递归实现
递归实现比较复杂,首先,为了得到所有不重复的集合,我们可以这么理解这个问题,把数组看成是不同球的编号,数组的大小为球的个数,不同的数字为不同的球,比如数组【1,1,2】就代表有两个标号为1的球和一个标号为2的球,现在的问题是一共要从中取出x个球,x<=n,组成一个子集,有多少种不同的子集。我们可以先把不同标号的球放到不同的袋子里,标号为1的球都放到标号为1的袋子里,标号为2的球都放到标号为2的袋子里,以此类推,最后我们假设得到了m个不同的带子,里面分别放了若干个球,接着我们要做的只是从这些袋子里面取x个球,一共有多少种组合就是所有的不重复的子集合数,取的过程中可以在每个袋子中取0到这个袋子中的球的个数个球。
先看主函数:
void subSetsNoDup(int a[],int n){
unordered_map<int,int> mymap;
for(int i = 0;i<n;i++){
if(mymap.find(a[i]) != mymap.end())
mymap[a[i]]++;
else
mymap[a[i]] = 1;
}
vector<pair<int,int>> elems;
for(auto i = mymap.begin();i!=mymap.end();i++)
elems.push_back(pair<int,int>(i->first,i->second));
sort(elems.begin(),elems.end());
vector<int> path;
subsets2(elems,0,path);
}
此函数前面部分都在分组和计算袋子中的球的数目,只有最后一个函数subsets2执行了取球的过程,下面看subsets2取球过程:
void subsets2(vector<pair<int,int>>& elems,int step,vector<int>& path){
if(step == elems.size()){
printPath(path);
return;
}
for(int i = 0;i<=elems[step].second;i++){
for(int j = 0;j<i;j++)
path.push_back(elems[step].first);
subsets2(elems,step+1,path);
for(int j = 0;j<i;j++)
path.pop_back();
}
}
step记录的是取到了第几个袋子,当取完所以袋子则返回,path是临时变量,记录的是当前的子集。在每次对某个袋子取球时,可以0到取这个袋子中所有球的个数个。每步取一个袋子,取了一个袋子之后递归取下一个袋子。
本人水平有限,文章有错误的地方欢迎大家指出,有问题也希望大家能够留言交流,一起学习进步