leetcode 78题 子集(c++ 三种解法)

本文深入探讨了子集生成算法的三种实现方法:使用深度优先搜索(DFS)、位操作(Bitmap)及递增子集构建策略。通过详细的代码示例和逻辑解析,帮助读者理解如何从一个整数数组生成其所有可能的子集。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

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

说明:解集不能包含重复的子集。

示例:

输入: nums = [1,2,3]
输出:
[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]

解题思路:

解题思路1(包含三种解题方法):这个我没看懂作者本人的解题思路,没看懂树是怎么建的

https://blog.youkuaiyun.com/camellhf/article/details/73551410

因此找到了解题思路2:是对解题思路1中作者的想法的一个详细讲解

https://www.cnblogs.com/ariel-dreamland/p/9154503.html

建立的树是这样的:(来源于第二个链接)

                        []
                     /      \
                    /        \
                   /          \        
                  /            \     
                 /              \
              [1]                []
           /       \           /    \
          /         \         /      \        
       [1 2]       [1]       [2]     []
      /     \     /   \     /   \    / \
  [1 2 3] [1 2] [1 3] [1] [2 3] [2] [3] []

第一层为空集,第二层为对第一个数的选择,以此类推。左节点代表选中,右节点代表不选。

代码:

方法1:用dfs解决,对应解题思路1的第一个方法

注意的点:

1.每次一个数字出栈就相当于选择了不选择它的路。

2.为什么从第一层开始循环?因为根节点不存在不选择它的情况。

3.为什么不需要存储不选择的情况?因为在存储上一层节点值的时候,实际上就是在存储不选择的情况,因为没到下一层相当于没选择。

class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>> result;
        vector<int> tmp;
        //sort(nums.begin(), nums.end());
        getSub(nums,result,0,tmp);
        return result;
    }
    void getSub(vector<int> s,vector<vector<int>> &result,int layer,vector<int> &tmp)
    {
        result.push_back(tmp);
        for(int i = layer;i < s.size();i++)
        {
            tmp.push_back(s[i]);
            getSub(s,result,i + 1,tmp);
            tmp.pop_back();
        }
    }
    
};

方法2:用bitmap解决,对应解题思路1的第二个方法

感觉原链接中的代码写的非常棒!

外层循环控制对数组中每个数字的选择,内层循环是从二进制数000到111所有情况的列举。

class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        
        int bNumber = pow(2,nums.size()); //把二进制数转化为十进制 为2^nums.size() 
        vector<vector<int>> result(bNumber);
        for(int i = 0;i < nums.size();i++)
            for(int j = 0;j < bNumber;j++)
                if(j >> i & 1) //j左移i位(从而判断数组中第i个元素是否加入子集),结果和1相与(从而只取最低位)
                    result[j].push_back(nums[i]);
        return result;
    }
    
};

方法3:对应解题思路1的第三个方法

即:

第二种解法让我很惊叹。它是发掘到了一个规律,集合中每添加一个元素,则子集数目增加一倍,且增加的子集为所有原始子集加上新的元素。举个例子:nums=[1,2,3] 
1. 初始时集合为空,子集为[ [] ]。 
2. 添加一个元素1,即集合为[1]时,子集为空集和空集+元素1,即[ [], [1] ]。 
3. 添加下一个元素2,集合为[1,2],子集除了包含上一步的所有集合还新增了对应集合+元素2的所有集合,即[ [], [1], [2], [1,2]],其中[2]是空集+元素2,[1,2]是[1]+元素2。 
4. 添加下一个元素3,集合为[1,2,3],类似的得到子集为[ [], [1], [2], [1,2], [3], [1,3], [2,3], [1,2,3] ],其中[3]是空集+元素3,[1,3]是[1]+元素3,[2,3]是[2]+元素3,[1,2,3]是[1,2]+元素3。
(来源于第一个链接)

 

class Solution {
public:
	vector<vector<int>> subsets(vector<int>& nums) {
		vector<vector<int>> result;
		vector<int> empty;
		result.push_back(empty);
		for (int i = 0; i < nums.size(); i++)
		{
			int result_size = result.size();//进入循环前存储在添加前的结果长度 因为添加后长度会变
			for (int j = 0; j < result_size; j++)
			{
				vector<int> tmp = result[j];
                tmp.push_back(nums[i]);
                result.push_back(tmp);
			}
		}
		return result;
	}

};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值