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], [] ]
1.基於DFS的遞歸
原數組中每一個元素在子集中有兩種狀態:要麼存在、要麼不存在。這樣構造子集的過程中每個元素就有兩種選擇方法:選擇、不選擇,因此可以構造一顆二叉樹來表示所有的選擇狀態:二叉樹中的第i+1層第0層無節點表示子集中加入或不加入第i個元素,左子樹表示加入,右子樹表示不加入。所有葉節點即爲所求子集。因此可以採用DFS的遞歸思想求得所有葉節點。
代碼如下:
//S爲原數組,temp爲當前子集,level爲原數組中的元素下標亦爲二叉樹的層數,result爲所求子集集合
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);
}
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 idx,int n)
{
vector<vector<int> > result;
if(idx == n)
{
vector<int> temp;
result.push_back(temp);
}
else
{
vector<vector<int> > vec = subsets(S,idx + 1,n);
int a = S[idx];
for(int i = 0; i < vec.size();i++)
{
vector<int> v = vec[i];
result.push_back(v);
v.push_back(a);
sort(v.begin(),v.end());
result.push_back(v);
}
}
return result;
}
3.位運算
求子集問題就是求組合問題。數組中的n個數可以用n個二進制位表示,當某一位爲1表示選擇對應的數,爲0表示不選擇對應的數。
vector<vector<int> > subsets(vector<int> &S,int n)
{
//n個數有0~max-1即2^n中組合,1<<n表示2^n
int max = 1<<n;
vector<vector<int> >result;
for(int i = 0;i < max;i++)
{
vector<int> temp;
int idx = 0;
int j = i;
while(j > 0)
{
//判斷最後一位是否爲1,若爲1則將對應數加入到當前組合中
if(j&1)
{
temp.push_back(S[idx]);
}
idx++;
//判斷了這一位是否爲1後要右移
j = j>>1;
}
//判斷完了一種組合,加入到結果集中
result.push_back(temp);
}
return result;
}