【LeetCode】求数组元素(可能有重复元素)的所有子集:Subsets II

Given a collection of integers that might contain duplicates, nums, return all possible subsets (the power set).

Note: The solution set must not contain duplicate subsets.

Example:

Input: [1,2,2]
Output:
[
  [2],
  [1],
  [1,2,2],
  [2,2],
  [1,2],
  []
]

1. 递归

1.1 增量构造法(版本1)

class Solution {
public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        vector<vector<int> > result;
        vector<int> subset;
        
        sort(nums.begin(), nums.end());
        subsetWithDup(nums, 0, result, subset);
        
        return result;
    }
    
    void subsetWithDup(vector<int>& nums, int start, vector<vector<int> >& result, vector<int>& subset) {
        result.push_back(subset);
        
        for(int i = start; i < nums.size(); i++) {
            // 在start到nums.size()的范围,一个数字连续两次出现,跳过,避免重复)
            if(i !=  start && nums[i] == nums[i - 1]) continue;
            
            subset.push_back(nums[i]);
            subsetWithDup(nums, i + 1, result, subset);
            subset.pop_back();
        }
    }
};

1.2 增量构造法(版本2)

  • 直接上代码
class Solution {
public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        vector<vector<int> > result;
        unordered_map<int, int> countMap; // 记录每个元素出现的次数
        
        sort(nums.begin(), nums.end());
        for(int i = 0; i < nums.size(); i++) {
            if(countMap.find(nums[i]) != countMap.end()) {
                countMap[nums[i]]++;
            } else {
                countMap[nums[i]] = 1;
            }
        }
        
        // 将map里的pair拷贝到vector中
        vector<pair<int, int> > elems;
        for(unordered_map<int, int>::iterator iter = countMap.begin(); iter != countMap.end(); iter++) {
            elems.push_back(pair<int, int>(iter->first, iter->second));
        }
        sort(elems.begin(), elems.end());
        
        vector<int> subset;
        subsetWithDup(elems, 0, result, subset);
        
        return result;
    }
    
    void subsetWithDup(vector<pair<int, int> >& elems, int start, vector<vector<int> >& result, vector<int>& subset) {
        if(start == elems.size()) {
            result.push_back(subset);
            return;
        }
        
        for(int i = 0; i <= elems[start].second; i++) {
            for(int j = 0; j < i; j++) {
                subset.push_back(elems[start].first);
            }
            subsetWithDup(elems, start + 1, result, subset);
            for(int j = 0; j < i; j++) {
                subset.pop_back();
            }
        }
    }
};

1.3 位向量法

class Solution {
public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        vector<vector<int> > result;
        
        // 计算每个元素的个数
        sort(nums.begin(), nums.end());
        vector<int> count(nums.back() - nums.front() + 1, 0);
        for(int i = 0; i < nums.size(); i++) {
            count[nums[i] - nums[0]]++;
        }
        
        // 每个元素选择了多少个
        vector<int> selected(nums.back() - nums.front() + 1, -1);
        subsetWithDup(nums, count, selected, 0, result);
        
        return result;
    }
    
    void subsetWithDup(vector<int>& nums, vector<int>& count, vector<int>& selected, int start, vector<vector<int> >& result) {
        if(start == count.size()) {
            vector<int> subset;
            for(int i = 0; i < selected.size(); i++) {
                for(int j = 0; j < selected[i]; j++) {
                    subset.push_back(i + nums[0]);
                }
            }
            result.push_back(subset);
            return;
        }
        
        for(int i = 0; i <= count[start]; i++) {
            selected[start] = i;
            subsetWithDup(nums, count, selected, start + 1, result);
        }
    }
};

2 迭代

2.1 增量构造法

基本思想

  • 拿题目中的例子 [1 2 2] 来分析,当处理完第一个2时,此时的子集合为 [], [1], [2], [1, 2],而这时再处理第二个2时,如果在 [] 和 [1] 后直接加2会产生重复,所以只能在上一个循环生成的后两个子集合后面加2,发现了这一点,题目就可以做了,我们用 last 来记录上一个处理的数字,然后判定当前的数字和上面的是否相同,若不同,则循环还是从0到当前子集的个数,若相同,则新子集个数减去之前循环时子集的个数当做起点来循环,这样就不会产生重复了. 代码如下:
class Solution {
public:
    vector<vector<int>> subsetsWithDup(vector<int> &nums) {
        if (nums.empty()) return {};
        
        vector<vector<int>> res(1, vector<int>());
        sort(nums.begin(), nums.end());
        
        int size = 1, last = nums[0];
        for (int i = 0; i < nums.size(); ++i) {
            if (nums[i] != last) {
                last = nums[i];
                size = res.size();
            }
            int newSize = res.size(); 
            // 当前元素和上一元素不相同时,size和newSize值相同
            // 当前元素和上一元素不相同时,只追加元素到上一个元素产生的自己中,生成新子集,再追加到结果集中
            for (int j = newSize - size; j < newSize; ++j) {
                res.push_back(res[j]);
                res.back().push_back(nums[i]);
            }
        }
        return res;
    }
};

2.2 二进制法

class Solution {
public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        set<vector<int> > result;
        int n = nums.size();
        
        for(int i = 0; i < pow(2, n); i++) {
            vector<int> subset;
            for(int j = 0; j < n; j++) {
                if(i & (1 << j))
                    subset.push_back(nums[j]);
            }
            result.insert(subset);
        }
        
        vector<vector<int> > real_result;
        copy(result.begin(), result.end(), back_inserter(real_result));
        
        return real_result;
    }
};

参考资料:

https://www.cnblogs.com/grandyang/p/4310964.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值