問題:給定一個可能具有重複數字的列表,返回其所有可能的子集。
樣例 1:
輸入:[0]
輸出:
[
[],
[0]
]
樣例 2:
輸入:[1,2,2]
輸出:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
思路:
- 使用回溯法的遞歸框架;
- 解空間爲子集樹;
- 對於集合中出現了重複的數字,需要設計剪枝函數check(i)。規定重複元素的選取狀態只能是前半部分1,後半部分0,從而排除重複項。例如【1,3,3,3】,對於重複元素3的選取,1 0 0結果是【3】,0 1 0結果也是【3】,0 0 1結果還是【3】。那麼只保留第一種情況,0後不能再取1就好了,遇到其餘情況便不再繼續下遞歸。
- 除此之外,還有另一位老哥的辦法,在去重複元素求子集的基礎上再一步一步添加重複元素,記錄一下。
跳轉到該鏈接
參考代碼:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100;
int v[maxn] = {0},x[maxn] = {0};
int backtrack(int i,int n);
int check(int i);
int main(){
printf("求n個元素子集,請輸入n的值\n");
int n;
scanf("%d",&n);
printf("請輸入集合元素\n");
for(int i = 1 ; i <= n ; i++ )
scanf("%d",&v[i]);
backtrack(1,n);
return 0;
}
int backtrack(int i,int n){
if(i > n ){
cout<<"{ ";
for(int tempi = 1;tempi <= n; tempi++){
if(x[tempi] == 1){
printf("%d",v[tempi]);
}
}
cout<<" }"<<endl;
}else{
for(int j = 0 ; j <= 1 ; j++){
x[i] = j;
if(check(i))
continue;
backtrack(i+1,n);
}
}
}
int check(int i){
if(i != 1 && v[i] == v[i-1] && x[i-1] == 0 && x[i] == 1)
return 1;
else
return 0;
}
LintCode參考代碼:
class Solution {
public:
vector<vector<int>> v;
vector<int> tempv;
int x[1000]={0};
vector<vector<int>> subsetsWithDup(vector<int> &nums) {
int n = nums.size();
sort(nums.begin(),nums.end());
backtrack(0,n,nums);
return v;
}
int backtrack(int i,int n,vector<int> nums){
if(i >= n ){
tempv.clear();
for(int tempi = 0;tempi < n; tempi++){
if(x[tempi] == 1)
tempv.push_back(nums[tempi]);
}
v.push_back(tempv);
}else{
for(int j = 0 ; j <= 1 ; j++){
x[i] = j;
if( i!=0 && nums[i] == nums[i-1] && x[i-1] == 0 && x[i] == 1)
continue;
backtrack(i+1,n,nums);
}
}
}
};