【回溯法】重复元素集合 求子集

问题:给定一个可能具有重复数字的列表,返回其所有可能的子集。
样例 1:
输入:[0]
输出:
[
  [],
  [0]
]
样例 2:
输入:[1,2,2]
输出:
[
  [2],
  [1],
  [1,2,2],
  [2,2],
  [1,2],
  []
]

思路:
  1. 使用回溯法的递归框架;
  2. 解空间为子集树;
  3. 对于集合中出现了重复的数字,需要设计剪枝函数check(i)。规定重复元素的选取状态只能是前半部分1,后半部分0,从而排除重复项。例如【1,3,3,3】,对于重复元素3的选取,1 0 0结果是【3】,0 1 0结果也是【3】,0 0 1结果还是【3】。那么只保留第一种情况,0后不能再取1就好了,遇到其余情况便不再继续下递归。
  4. 除此之外,还有另一位老哥的办法,在去重复元素求子集的基础上再一步一步添加重复元素,记录一下。
    跳转到该链接
参考代码:
#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:
    /**
     * @param nums: A set of numbers.
     * @return: A list of lists. All valid subsets.
     */
    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);
		}
	}
}
};
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值