leetcode 78. 子集(无重复元素)+leetcode 90. 子集 II(有重复元素)

124 篇文章 1 订阅
63 篇文章 0 订阅

78.子集

在这里插入图片描述

方法一:位运算

集合子集中的元素与子集下标中二进制位的“1”相对应,因此求子集中元素的运算可以转化为寻找子集
下标二进制位中“1”所在位置的运算。此运算可以利用移位操作来实现。例如.求101所对应的子集,我们可以通过3次移位,每次先将当前的数值与数值1相与,如果结果等于1,则本次第0位上的值为1,否则为0.由移位的次数可以知道当前第0位的1在原数据中的位置.从而找到对应的实际元素。运算如下:

#include <iostream>
#include <cstdio>
using namespace std;

/*程序用途:输入一个n,打印集合{0,1,2,3,...,n-1}的所有子集(包含空集和本身)*/
//原理:若集合大小为n,则其子集的大小为2的n次方(包括空集和本身)
//举例 n=3,则{0,1,2}的子集为{},{0},{1},{2},{0,1},{0,2},{1,2},{0,1,2}.空集没有显示出来
//则咱们可以用000表示{},001表示{0},010表示{1},100表示{2},011表示{1,0},111表示{2,1,0},其他你懂的   
int main()
{
	int n;
	cin >> n;
	//二进制1向右移n位 
	for (int i = 0; i<(1 << n); i++)			//1<<n是2的n次方,用i生成000,001,010,011,100...111的序列 
	{
		for (int j = 0; j<n; j++)			//用(1<<j)生成001,010,100的序列
		{
			if (i&(1 << j))				//相与,i在j位上是否为1 
			{
				printf("%d ", j);
			}
		}
		printf("\n");
	}
	system("pause");
	return 0;
}

在这里插入图片描述
这个理解起来有点复杂

ij输出
0(000)0空集
1(001)10(第0位是1)
2(010)11(第1位是1)
3(011)0,10,1(第0,1位是1)
4(100)22(第2位是1)
5(101)0,20,2(第0,2位是1)
6(110)1,21, 2(第1,2位是1)
7(111)0,1,20,1, 2(第0,1,2位是1)

这个写成class为:

class Solution {
public:
	vector<int> t;
	vector<vector<int>> ans;

	vector<vector<int>> subsets(vector<int>& nums) {
		int n = nums.size();
		for (int mask = 0; mask < (1 << n); ++mask) {
			t.clear();
			for (int i = 0; i < n; ++i) {
				if (mask & (1 << i)) {
					t.push_back(nums[i]);
				}
			}
			ans.push_back(t);
		}
		return ans;
	}
};


在这里插入图片描述

方法二:回溯

https://leetcode-cn.com/problems/subsets/solution/c-zong-jie-liao-hui-su-wen-ti-lei-xing-dai-ni-gao-/
这个讲的特别好

回溯模板

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}

解题步骤如下:
在这里插入图片描述

//nums为题目中的给的数组
//path为路径结果,要把每一条 path 加入结果集
void backtrack(vector<int>nums,vector<int>&path,int start)

在这里插入图片描述

//res为结果集,是全局变量vector<vector<int>>res,到时候要返回的
res.push_back(path);//把每一条路径加入结果集

在这里插入图片描述

for(int i=start;i<nums.size();i++)

在这里插入图片描述

void backtrack(vector<int>nums,vector<int>&path,int start)
{
    for(int i=start;i<nums.size();i++)
    {
        path.push_back(nums[i]);//做出选择
        backtrack(nums,path,i+1);//递归进入下一层,注意i+1,标识下一个选择列表的开始位置,最重要的一步
    }
}

在这里插入图片描述

void backtrack(vector<int>nums,vector<int>&path,int start)
{
    res.push_back(path);
    for(int i=start;i<nums.size();i++)
    {
        path.push_back(nums[i]);//做出选择
        backtrack(nums,path,i+1);
        //递归进入下一层,注意i+1,标识下一个选择列表的开始位置,最重要的一步
        path.pop_back();//撤销选择
    }
}

最后代码为:

class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& nums, int startIndex) {
        result.push_back(path); // 收集子集,要放在终止添加的上面,否则会漏掉自己
        if (startIndex >= nums.size()) { // 终止条件可以不加
            return;
        }
        for (int i = startIndex; i < nums.size(); i++) {
            path.push_back(nums[i]);
            backtracking(nums, i + 1);
            path.pop_back();
        }
    }
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        result.clear();
        path.clear();
        backtracking(nums, 0);
        return result;
    }
};


作者:carlsun-2
链接:https://leetcode-cn.com/problems/subsets/solution/dai-ma-sui-xiang-lu-78-zi-ji-hui-su-sou-6yfk6/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

自己写的

class Solution {
private:
    vector<vector<int> > res;
    vector<int> path;
public:
    void dfs(vector<int>& nums,vector<int> &path,int n){
        res.push_back(path);
        for(int i=n;i<nums.size();i++){
            path.push_back(nums[i]);
            dfs(nums,path,i+1);
            path.pop_back();
        }
    } 
    vector<vector<int>> subsets(vector<int>& nums) {
        dfs(nums,path,0);
        return res;
    }
};

90.子集II

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

void backtrack(vector<int>& nums,vector<int>&path,int start)
    {
        res.push_back(path);
        for(int i=start;i<nums.size();i++)
        {
            if(i>start&&nums[i]==nums[i-1])//剪枝去重
                continue;
        }
    }

在这里插入图片描述

void backtrack(vector<int>& nums,vector<int>&path,int start)
    {
        res.push_back(path);
        for(int i=start;i<nums.size();i++)
        {
            if(i>start&&nums[i]==nums[i-1])//剪枝去重
                continue;
            temp.push_back(nums[i]);
            backtrack(nums,path,i+1);
        }
    }

在这里插入图片描述

** sort(nums.begin(),nums.end());
void backtrack(vector<int>& nums,vector<int>&path,int start)
    {
        res.push_back(path);
        for(int i=start;i<nums.size();i++)
        {
            if(i>start&&nums[i]==nums[i-1])//剪枝去重
                continue;
            temp.push_back(nums[i]);
            backtrack(nums,path,i+1);
            temp.pop_back();
        }
    }

所有代码为:

class Solution {
private:
    vector<vector<int> > res;
    vector<int> path;
public:
    void dfs(vector<int>& nums,vector<int> &path,int n){
        res.push_back(path);
        for(int i=n;i<nums.size();i++){
            if(i>n&&nums[i]==nums[i-1])//剪枝去重
                continue;
            path.push_back(nums[i]);
            dfs(nums,path,i+1);
            path.pop_back();
        }
    } 
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        sort(nums.begin(), nums.end()); //排序
        dfs(nums,path,0);
        return res;
    }
};

比上一个题增加了 剪枝去重和排序两个部分

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
重复元素子集问题是指给定一个可能包含重复元素的整数数组,要求返回所有不重复子集。关于这个问题的解决方法,可以采用深度优先遍历DFS法。 具体做法是将数组中的每个元素看作二叉树的一个节点,通过DFS的方式逐个遍历每个节点。与遍历二叉树的所有路径类似,但需要在递归右子树之前将新添加的元素弹出,以避免重复。 这个问题和其他一些问题,比如两个数字相加、无重复字符的最长子串、两个有序数组的中位数等类似,也可以通过递归和循环来解决。使用for循环的原因是,在多叉树的情况下,可以通过循环遍历每个子节点的选择列表,然后将选择的子节点作为当前节点进行递归调用。而在二叉树的情况下,则可以使用两个并列的递归。 总结起来,重复元素子集问题可以通过深度优先遍历DFS法来解决,也可以使用递归和循环的方式来求解。在实际编程中,可以根据具体情况选择合适的方法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [leetcode 78.不含重复元素数组的子集](https://blog.csdn.net/zy450271923/article/details/105701584)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [lrucacheleetcode-Leetcode-Questions:面试编码问题](https://download.csdn.net/download/weixin_38694800/19916021)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值