90. Subsets II

Given a collection of integers that might contain duplicates, nums, return all possible subsets.

Note: The solution set must not contain duplicate subsets.

For example,
If nums = [1,2,2], a solution is:

[
  [2],
  [1],
  [1,2,2],
  [2,2],
  [1,2],
  []
]

这道题牵扯一点数学知识。

求子集,首先我们来看一个证明:

n=1时 明显有两个子集
假设n=k,k>=2时有2^k个子集
则n=k+1时 
新增加的元素与原来的子集又可构成2^k个子集
故此时总共有2^k+2^k=2^(k+1)个子集
故该命题在n>=2时也成立
故命题正确


集合中的每个元素都有两种可能,在集合或不在集合,好像0和1,那么n个元素构成的子集数目就是2^n。

所以对于重复元素,我们可以把它看成一个特殊元素,举例:1, 2, 2 ,元素2具有不在集合,第一个2在集合中,第二个2在集合中三种可能,但我们把它看成一个元素,理解成一个大“2”。所以,我们要对该特殊元素配备一个计数器,用来计数它的数目。普通元素只加入集合一次就可以了,该特殊元素可以加入多次。

我们对这道题首先需要排序,不然难以找到相同元素。

将计数器count初始化为1,因为任意普通元素也至少要加入集合一次。当重复多次时,++count,下次直接跳过重复元素就可以,因为把它看成了一个整体。

代码如下:

class Solution {
public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        vector<vector<int>> totalset = {{}};
        const int size = nums.size();
        sort(nums.begin(), nums.end());    /可不要忘记排序
        for(int i=0; i<size; ){
            int count = 1;
            while(i+count<size && nums[i] == nums[i+count])
                ++count;
            int prev_size = (int)totalset.size();
            for(int j=0; j<prev_size; ++j){
                vector<int> vec = totalset[j];
                for(int k=0; k<count; ++k){
                    vec.push_back(nums[i]);
                    totalset.push_back(vec);
                }
            }
            i += count;
        }
        return totalset;
    }
};


另外一种思路,回溯法:

先深入进去,没路了再回来。

该方法打印顺序为,以[1, 2, 2]为例:

空, 1, 12,122,2, 22

起始这种方法就和我们最普通的形式差不多,从第一个元素开始,打印长度为1,长度为2,...,然后再从第二个元素继续。。。 

class Solution {
public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        vector<vector<int>> totalset = {{}};
        vector<int> vec;
        const int size = nums.size();
        sort(nums.begin(), nums.end());
        handle_detail(totalset, nums, vec, size, 0);
        return totalset;
    }
private:
    void handle_detail(vector<vector<int>>& totalset, const vector<int>& nums, 
                        vector<int>& vec, const int size, int cur){
        for(int i=cur; i<size; ++i){
            if(i > cur && nums[i] == nums[i-1])
                continue;
            vec.push_back(nums[i]);
            totalset.push_back(vec);
            handle_detail(totalset, nums, vec, size, i+1);
            vec.pop_back();
        }

    }
};


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值