子集(2)(含重复数字)

题目描述

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

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

示例

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

思路

/**
 * 这个题目的解题思想是这样的。由于nums内的元素存在重复,那么我们必然需要
 * 考虑如果元素重复了,怎么去处理这个元素。最直观的想法是说比如我碰到了
 * nums[i] == nums[i-1]表示当前元素我处理过了,拿题目中的数组举例子
 * nums:[1,2,2]  i=2的时候就满足上式,那么我们可以认为这个元素已经处理过了
 * 就直接跳过吗? 显然不能 因为如果直接跳过我们就会漏掉[2,2] 和 [1,2,2]
 * 这两个组合。那么说明我们必须找出某种方式,将部分重复的元素去除。
 * 
 * 我们仔细思考一下nums[1,2,2] 当i=0的时候由于我们用于存储所有已知集合的
 * retList只含有一个[]元素,那么不存在重复问题,我们经过这一步可以得到
 * retList: [] [1] 来到2的时候我们在看 由于也不存在重复我们的2可以和
 * 之前的retlist中的元素全组合一遍得到retList:[] [1] [2] [1,2]
 * 等i=2来到这个重复的2的时候,我们发现他和前面的元素重复了,那么如果我们
 * 先不考虑重复的问题重复会得到[] [1] [2] [1,2] [2] [1,2] [2,2] [1,2,2]
 * 我们发现[2] [1,2]这部分是重复的部分是需要被踢出的部分 那么我们的目标现在
 * 就转变成了如何鉴别出引起重复的这一部分,然后在组合的时候跳过他们。我们回忆一下
 * 重复的这个[2] [1,2]来源于 2 这个元素和 [] [1] 组合导致的,因为在这个重复的
 * 2之前,已经有一个2和[] [1]发生过组合,所以这里再去组合 必然发生重复现象。
 * 那实际上这个第二次出现的2,只应该和[2] [1,2]发生组合。在这个例子中[2] [1,2]
 * 是两个组合,很容易看出来,但是我们需要一个值,来表示说出现重复时我到底该匹配的值
 * 有多少个? 这个值就是上一次没有出现重复元素时,retList的长度。这么说太抽象了
 * 我们举个例子 假设我们来到了[] [1] 现在2要和他们进行组合 此时2和1不相同,那么
 * 他应该和整个retList进行组合 需要进行组合的元素数为2.那么当 来到第二个2时,此时
 * retList中有四个元素[] [1] [2] [1,2] 按照刚才我们说的他只可以组合两个元素,
 * 否则必然引起重复,而且是从后往前数两个元素(这个方向是因为,新的组合总是添加在数组的
 * 尾巴上),如果照我们说的 他只应该和[2],[1,2]发生组合最后的出[] [1] [2] [1,2] [2,2] [1,2,2].
 * 
 * 接下来说点别的,为什么第二个重复元素只能去和倒数的 上一次没有出现重复元素时,retList的长度个
 * 元素进行组合?
 * 
 * 原因是这样的,比如当nums[i] != num[i-1]时,此时nums[i]需要和retList中所有元素进行组合
 * 该过程完成后retList的大小会由原大小m 变化为2m。当我们继续往后走时,当前nums[i] == nums[i-1]
 * 我们直到我们当前的nums[i]只应该和之前的nums[i-1]没处理过的部分,或者之前的nums[i-1]在上一次
 * 组合中新生成的部分进行组合(否则必然造成重复),那这个新生成部分的大小是多少呢?答案是m,因为再不重复时
 * 每一次的组合结束大小都会变为原来的1倍,一半是之前的值,一半是新生成的值,而这个m就是上一次没有出现重复元素时,retList的长度。
 * 
 * 以此类推 当我们的nums[1,2,2,2] 当i=3时,这个时候他还是只需要和上一次retList的最后m个元素进行组合
 * 
 *  
*/

实现

vector<vector<int>> subsetsWithDup(vector<int>& nums) {
	vector<vector<int>> res;
	vector<int> tmp;
	res.push_back(tmp);
	if (nums.size() == 0)
	{
		return res;
	}

    sort(nums.begin(), nums.end());
    
	tmp.push_back(nums[0]);
	res.push_back(tmp);
	tmp.clear();
	if (nums.size() == 1)
	{
		return res;
	}

	int lastLen = 1;

	for (int i = 1; i < nums.size(); i++)
	{
		int size = res.size();
		if (nums[i] != nums[i - 1])
		{
			lastLen = size;
		}

		for (int j = size - lastLen; j < size; j++)
		{
			vector<int> inner = res[j];
			inner.push_back(nums[i]);
			res.push_back(inner);
		}
	}

	return res;

}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值