子集生成:递归、迭代、二进制

17 篇文章 0 订阅
9 篇文章 0 订阅

子集生成,给定一个数组,数组中包含一些元素(简单起见,假设其中包含的是数字)
生成该集合所有的子集。
比如:{1, 2, 3}
子集为{{}, {1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}, {1, 2, 3}}

本题比较简单,有点像01背包问题。解题思路如下:
首先,将空集合放入结果集。然后遍历源集合中的每个元素,往空集合中加还是不加。话说不太明白,举个例子

{a, b, c}
首先结果集合中添加{}空集合
然后将结果集中的元素去除,对第一个源集合中的元素,也就是a,进行选择,添加到{}中就变为{a}, 不添加就是{}, 然后将这两个结果,添加到最终结果中去,此时最终结果集合中的元素为{{},{a}}。
按照上面的步骤,将{}, {a}取出,处理b,对于{}来说,有添加进去和不添加进去,分别为{}, {b},对于{a}来说也一样有添加和不添加,结果为{a}, {a, b},最后将这四个结果添加进结果集合,此时结果集合变为{{},{b}, {a}, {a, b}}
重复上述思想,即可求得结果

如果还不是太清楚怎么操作分析代码是最好的办法:
我分别采用了递归、迭代和二进制解决此题。

// 递归
set<set<int>> helper1(const vector<int> &src, int cur){//cur当前处理节点的index,从后往前处理,因为是递归,先拿到[]集合
    if(0 == cur){
        set<set<int>> finalSet;
        finalSet.insert(set<int>());
        set<int> tempSet;
        tempSet.insert(src[cur]);
        finalSet.insert(tempSet);
        return finalSet;
    }
    set<set<int>> newSet_ = helper1(src, cur - 1);
    set<set<int>> newSet = newSet_;
    for(auto eachSet : newSet){
        eachSet.insert(src[cur]);
        newSet.insert(eachSet);//加
    }
    return newSet;
}

// 迭代
set<set<int>> helper2(const vector<int> &src){
    set<set<int>> finalSet;
    finalSet.insert(set<int>());//首先将空集合加进去

    for(int a : src){
        set<set<int>> tempDup = finalSet;
        for(auto each : tempDup){
            each.insert(a);//加
            finalSet.insert(each);
        }
    }
    return finalSet;
}

// 二进制
set<set<int>> helper3(const vector<int> &src){
    set<set<int>> finalSet;
    int size = src.size();
    for(int i = pow(2, size) - 1; i >= 0; --i){//111到000
        //对每一个都左移size次
        set<int> midRes;
        int index = 0;
        while(index < size){
            if(i&(1<<index)){
                midRes.insert(src[index]);//index表示某位为1时,拿到该下标的元素
            }
            ++index;
        }
        finalSet.insert(midRes);
    }
    return finalSet;
}
int main(){
    vector<int> src{1, 2};
    // set<set<int>> res = helper1(src, src.size() - 1);
    // set<set<int>> res = helper2(src);
    set<set<int>> res = helper3(src);
    for(auto each : res){
        if(each.empty()){
            cout<<"nil"<<endl;
            continue;
        }
        for(int a : each){
            cout<< a <<" ";
        }
        cout<< endl;
    }

    return 0;
}

说明一下二进制:

A B C
0 0 0
0 0 1
0 1 0
0 1 1
1 0 0
1 0 1
1 1 0
1 1 1

其中1表示选择对应的元素,0表示不选,再结合代码很容易想到处理思路。

写的比较匆忙,如有问题还望指正!

除二进制外,可以抽象为对每个元素选还是不选,所以可以在构造一个选与不选的树,如下图
在这里插入图片描述
代码如下:
leetcode链接

class Solution {
public:
    vector<vector<int>> ans;
    void helper(vector<int>& nums, vector<int> &res, int index){
        if(index >= nums.size()){
            ans.push_back(res);
            return;
        }
        // 不选
        helper(nums, res, index + 1);
        // 选
        res.push_back(nums[index]);
        helper(nums, res, index + 1);
        res.pop_back();
    }
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<int> res;
        helper(nums, res, 0);
        return ans;
    }
};
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值