Given a set of distinct integers, S, return all possible subsets.
Note:
- Elements in a subset must be in non-descending order.
- The solution set must not contain duplicate subsets.
For example,
If S = [1,2,3]
, a solution is:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
这道题目有4种方法,下面分别阐述:
1. 基于二叉树的递归算法
我们可以构造一棵二叉树,树的层数为S.size()+1,根节点为空,其左右节点分别表示 不加入 和 加入S中第一个元素。以此类推,第 i 层的节点的2个子节点(第i+1层),分别表示不加入S[i] 和 加入S[i] 。
void subsets(vector<int> &S,vector<int> temp,int level,vector<vector<int> > &result)
{
//如果是叶子节点则加入到result中
if(level == S.size())
{
result.push_back(temp);
return;
}
//对于非叶子节点,不将当前元素加入到temp中
subsets(S,temp,level + 1,result);
//将元素加入到temp中
temp.push_back(S[level]);
subsets(S,temp,level + 1,result);
}
注意,此时temp是按值传入,函数中会保存temp的一份拷贝,如果改为传引用,则代码为:
void subsets(vector<int> &S,vector<int> &temp,int level,vector<vector<int> > &result)
{
//如果是叶子节点则加入到result中
if(level == S.size())
{
result.push_back(temp);
return;
}
//对于非叶子节点,不将当前元素加入到temp中
subsets(S,temp,level + 1,result);
//将元素加入到temp中
temp.push_back(S[level]);
subsets(S,temp,level + 1,result);
temp.pop_back();
}
2. 一般递归法
对于一个问题,如果我们能找到比原问题规模小却同质的问题,都可以用递归解决。比如要求{1, 2, 3}的所有子集,可以先求{2, 3}的所有子集,{2, 3}的子集同时也是{1, 2, 3} 的子集,然后我们把{2, 3}的所有子集都加上元素1后(注意排序),又得到同样数量的子集, 它们也是{1, 2, 3}的子集。这样一来,我们就可以通过求{2, 3}的所有子集来求 {1, 2, 3}的所有子集了。即为求1,2,3的子集,要先求2,3的子集,然后再把1加入到2,3的子集中去,典型的递归思路。代码如下:
vector<vector<int> > subsets(vector<int> &S,int ind,int vecSize)
{
vector<vector<int> > result;
sort(S.begin(),S.end(),cmp);
if (ind == vecSize)
{
vector<int> temp;
result.push_back(temp);
}
else
{
vector<vector<int> > vec = subsets(S,ind+1,vecSize);
for (int i=0;i<vec.size();i++)
{
result.push_back(vec[i]);
vec[i].push_back(S[ind]);
result.push_back(vec[i]);
}
}
return result;
}
3. 基于二进制位运算的方法
本方法的前提是:集合的元素不超过int 位数。用一个int 整数表示位向量,第i 位为1,则表示选择S[i],为0 则不选择。例如S={A,B,C,D},则0110=6 表示子集{B,C}。
这种方法最巧妙。因为它不仅能生成子集,还能方便的表示集合的并、交、差等集合运算。二进制法,也可以看做是位向量法,只不过更加优化。
vector<vector<int>> subsets2(vector<int>& S)
{
size_t n = S.size();
int maxNum = 1<<n;
vector<vector<int> > result;
vector<int> temp;
for (int i=0;i<maxNum;i++)
{
for (int j=0;j<n;j++)
{
int num=i;
if ((num>>j)&1) //改变原始数字,每次移动1位
{
temp.push_back(S[j]);
}
}
result.push_back(temp);
temp.clear();
}
return result;
}
或者
vector<vector<int>> subsets(vector<int>& S)
{
size_t n = S.size();
int maxNum = 1<<n;
vector<vector<int> > result;
vector<int> temp;
for (int i=0;i<maxNum;i++)
{
for (int j=0;j<n;j++)
{
if (i&1<<j) //原始数字不变,移动j
{
temp.push_back(S[j]);
}
}
result.push_back(temp);
temp.clear();
}
return result;
}
4.迭代法
第1次构造一个空集合{ }
第2次构造一个空集合{ },{S[0]}
第3次构造一个空集合{ },{S[0]},{ s[1]},{S[0],S[1]}
....
每一次把上一次的集合复制2遍,然后在后半部分加入当前的S[i]
vector<vector<int>> subsets(vector<int> &S)
{
sort(S.begin(),S.end());
vector<vector<int>> result(1);
for (int i=0;i<S.size();i++)
{
int size = result.size();
for (int j=0;j<size;j++)
{
result.push_back(result[j]);
result.back().push_back(S[i]);
}
}
return result;
}