(递归+回溯)leetcode中等90. 子集 II

题目

给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

示例 1:
输入:nums = [1,2,2]
输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]

示例 2:
输入:nums = [0]
输出:[[],[0]]

提示:

1 <= nums.length <= 10
-10 <= nums[i] <= 10

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/subsets-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

分析

解读题意,给定一个数组,返回一个二维数组,要求数组中包括所有给定数组的子集,注意子集不能有重复结果,例如[1,2,2],子集包括两个[1,2]返回的数组中只能包括一个
先不考虑重复的情况,我们可以想到从数组中的第一个元素出发,此时有两种选择,拿这个数字或者不拿,可以想到利用递归一种情况是拿,然后回溯(不拿)在深入遍历第二种情况
考虑重复的情况,我们可以利用set进行去重,考虑一种情况[2,1,2]此时它的子集包括[2,1],[1,2]这种情况下只利用set是没办法判断重复的,所以我们在进行递归之前要把给定的数组进行排序,换成[1,2,2]就可以去除多余的[1,2]

代码部分

初始化

考虑需要的变量(递归的参数),首先我们需要一个一维数组用来存储当前拿的数字,然后需要一个set<vector >的数组用来对当前的数组进行判断是否跟之前的数组重复,最后我们需要一个存放答案的数组。存放答案的数组要先把当前空数组存进去,因为空数组也是一种子集

		set<vector<int> > ans_set;		//用来去重 
		vector<int> nowarr;				//用来存放当前拿出来的数字 
		vector<vector<int> > ans;		//用来存放答案 
		ans.push_back(nowarr);			//首先把空数组存进答案 
递归+回溯

我考虑递归分为两步,首先思考当前能做的事情,最后思考出口条件

现在做的事情(递归+回溯)

首先将当前的数字存入数组中,然后在判断这个子集是否出现过,如果没有出现过,就把它记录在答案中,然后直接递归遍历下面的数,遍历完需要回溯(没拿当前数字的情况),然后再递归深入遍历

		//现在能做的事情
		nowarr.push_back(nums[i]);
		if(ans_set.find(nowarr)==ans_set.end())
		{
			ans.push_back(nowarr);
			ans_set.insert(nowarr);			
		}
		
		recursion_back(nums,nowarr,ans_set,ans,i+1);
		nowarr.pop_back();		//回溯(不拿当前的数字) 
		recursion_back(nums,nowarr,ans_set,ans,i+1);
递归出口

很简单,当下标的值等于给定数组的大小就返回就好了

		//出口
		if(i>=nums.size())
			return;

完整代码

class Solution {
public:
	//nowarr:当前存放数字的数组 ans存放答案 i存放下标 
	void recursion_back(vector<int>& nums,vector<int> nowarr, 
	set<vector<int> > &ans_set,vector<vector<int> > &ans,int i)
	{
		//出口
		if(i>=nums.size())
			return;
		//现在能做的事情
		nowarr.push_back(nums[i]);
		if(ans_set.find(nowarr)==ans_set.end())
		{
			ans.push_back(nowarr);
			ans_set.insert(nowarr);			
		}
		
		recursion_back(nums,nowarr,ans_set,ans,i+1);
		nowarr.pop_back();		//回溯(不拿当前的数字) 
		recursion_back(nums,nowarr,ans_set,ans,i+1);
	}
	
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
		set<vector<int> > ans_set;		//用来去重 
		vector<int> nowarr;				//用来存放当前拿出来的数字 
		vector<vector<int> > ans;		//用来存放答案 
		ans.push_back(nowarr);			//首先把空数组存进答案 
		
		sort(nums.begin(),nums.end());
		
		recursion_back(nums,nowarr,ans_set,ans,0);
		
		return ans;
    }
};

总结

什么样的情况下用递归呢,像这道题,我们如果直接用循环做,我们知道循环的次数不是很容易,一般不容易知道循环次数的题目,直接用递归会方便一些。
如果用循环这道题我的思路是状压,用一个bool类型的数组,每一位0或1代表当前的数字是否要拿

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

White boy&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值