leetcode 90. 子集 II

给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。

示例:

输入: [1,2,2]
输出:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]

一:迭代
思路: 先排序,在当前所有子集的基础上添加新的元素并构成新的子集,但这样会导致重复构建,为避免重复,需要一个指针标记新增子集的索引位置

借图一用
橘色表示新增子集,黑色表示重复子集,由于数字2的重复,在第四行开始之前,需要标记上一行(第三行)新增的子集的起始处,也就是第三行橘黄色的[ 2 ],在其及其之后的子集上添加2即可,

class Solution {
public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        vector<vector<int>> res;
        res.push_back({});
        int neNum = 0;   //新增子集的数目
        int start = 0;   //指针位置
        for(int i=0; i<nums.size(); i++){
            int size = res.size();
            //定位start起始位置
            if(i > 0 && nums[i] == nums[i-1]){
                start += neNum;
            }
            else start = 0;
            //指针及其之后的子集
            for(int j=start; j<size; j++){
                vector<int> ans = res[j];
                ans.push_back(nums[i]);
                res.push_back(ans);
            }
            //新增子集个数
            neNum = (res.size() - start)/2;
        }
        return res;
    }
};

二:深搜

class Solution {
public:
    vector<vector<int>> res;
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        vector<int> ans;
        dfs(nums, ans, 0);
        return res;
    }

    void dfs(vector<int>& nums, vector<int>& ans, int start){
        res.push_back(ans);
        for(int i=start; i<nums.size(); i++){
            //剪枝,如果连续相同的数,只需取其一,并且取优先出现的那个
            if(i > start && nums[i] == nums[i-1]){
                continue;
            }
            ans.push_back(nums[i]);
            //start参数即为递归出口
            dfs(nums, ans, i+1);
            ans.pop_back();  //回溯
        }
    }
};

三:位运算
对于数组中的每一位数,只有取与不取的选择,取为1,不取则为0;
所以共有2的n次方个结果数,(n为数组长度)
比如:[1 2 2],对于子集[1,2],也就是说对于连续相同的数,只需要取第一个2或者第二个2即可,二者选其一,既然这样,不如优先选择第一个2,
第一行表示三个数,第二行,第三行表示对于子集[1, 2]的两种选择

122
110
101
class Solution {
public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        vector<vector<int>> res;
        int totalNum = 1<<nums.size();
        for(int i=0; i<totalNum; i++){
            vector<int> ans;
            bool flag = false;
            //遍历,判断每一位是否选取
            for(int j=0; j<nums.size(); j++){
                //判断每一位是否选取,与1相与,若当前位是1,则选取
                if((i>>j&1)==1){
                    //当前是重复数字,并且前一位是 0,跳过这种情况
                    if(j>0 && nums[j]==nums[j-1] && (i>>(j-1)&1)==0){
                        flag = true;
                        break;
                    }else{
                        ans.push_back(nums[j]);
                     }
                }
            }
            if(!flag){
                res.push_back(ans); 
            }
        }
        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值