Subsets II -- leetcode

Given a collection of integers that might contain duplicates, 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,2], a solution is:

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


算法一,递归回溯

首先进行排序。

第一层底归,完成长度为1的所有子集

第二层底归,完成长度为2的所有子集

。。。

在每层递归中,依次添加剩余字符的组合。遇到重复字符,则跳过。

在leetcode上实际执行时间为13ms。

class Solution {
public:
    vector<vector<int> > subsetsWithDup(vector<int> &S) {
        sort(S.begin(), S.end());
        vector<vector<int> >ans(1);
        vector<int> sample;
        helper(ans, S, 0, sample);
        return ans;
    }
    
    void helper(vector<vector<int> > &ans, const vector<int> &S, int start, vector<int> &sample) {
        if (start == S.size()) return;
        
        for (int i=start; i<S.size(); i++) {
            sample.push_back(S[i]);
            ans.push_back(sample);
            helper(ans, S, i+1, sample);
            sample.pop_back();
            while (i+1 < S.size() && S[i] == S[i+1]) i++;
        }
    }
};


算法二  统计重复数字个数

如果不考虑重复数字,那么总的子集数,应该是2^n。

即,对于每个一字符,都有2种情况共选择,放入子集,或者不放入子集。故是2^n个子集。

对于重复数字的情况,可以把他们当成一个整体来考虑。

假设一个数字重复3次,那么对于这个特殊整体,就会有4种情况, 即, 不放入,放入1个,放入2个,放入3个。

同理,对于重复次数为n, 就有n+1种情况。


下面代码,也有DP的意味, 即已经知道n-1个字符的子集全集,求了n个字符的子集全集。

先统计出第n个字符的重复次数。

在已有的集合中,对每个子集,进行上面描述的放入操作。

下列代码,在leetcode上实际执行时间为15ms。

class Solution {
public:
    vector<vector<int> > subsetsWithDup(vector<int> &S) {
        sort(S.begin(), S.end());
        vector<vector<int> >ans(1);
        for (int i=0; i<S.size(); i++) {
            int count = 1;
            while (i+1 < S.size() && S[i] == S[i+1]) {
                count++;
                i++;
            }
            
            const int size = ans.size();
            for (int j=0; j<size; j++) {
                ans.push_back(ans[j]);
                ans.back().push_back(S[i]);
                for (int k=1; k<count; k++) {
                    ans.push_back(ans.back());
                    ans.back().push_back(S[i]);
                }
            }
        }
        
        return ans;
    }
};

该算法思路参考自:

https://leetcode.com/discuss/14902/c-solution-and-explanation



算法三 逐个字符添加

这个思路也类似于DP。

已知n-1个字符的子集的全集,求n个字符的子集的全集。

方法是,对已有的每个子集,复制一份,并添加该新字符。

对于后续重复的字符, 则只复制上次新增子集,并添加该字符。

本代码,在leetcode上实际执行时间为14ms。

class Solution {
public:
    vector<vector<int> > subsetsWithDup(vector<int> &S) {
        sort(S.begin(), S.end());
        vector<vector<int> >ans(1);
        int last_size = 0;
        for (int i=0; i<S.size(); i++) {
            const int start = i && S[i]==S[i-1] ? last_size : 0;
            last_size = ans.size();
            for (int j=start; j<last_size; j++) {
                ans.push_back(ans[j]);
                ans.back().push_back(S[i]);
            }
        }
        return ans;
    }
};


该算法参考自:

https://leetcode.com/discuss/11905/simple-iterative-solution

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值