Leetcode:90. 子集 II 题解

很久没更新了,主要是刷的简单题也没必要放在博客上。博主已经默默连续刷题快一个月了。今天遇到了个跟《代码随想录》题解不一样的思路、且更有效率,分享给大家~
在这里插入图片描述

题目

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

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

链接:90. 子集 II - 力扣(LeetCode)

思路

重点在于如何 去重 。如nums = [1, 2, 2, 3],为了区别第二个2和第三个2,我们标记为nums = [1, 2a, 2b, 3]。

题目所说的不包含重复子集,意思是[2a, 3]和[2b, 3]不能同时出现,[1, 2a, 3]和[1, 2b, 3]不能同时出现,但是[1, 2a, 2b]是允许的。

如果不考虑重复,正常思路是使用回溯算法,通过枚举的方法找出所有答案。为了不出现重复子集,需要对算法进行剪枝操作。

因此,如何剪枝呢?

用《代码随想录》里的图(代码随想录 (programmercarl.com))来举例(忽略used数组,这里不使用used数组解题):

回溯算法有两个方向:纵向和横向。纵向是不断取值,寻找符合条件的组合;横向是更换取值。

先给出正常枚举、不考虑去重的代码(重点关注trackbacking函数):

class Solution(object):
    def __init__(self):
        self.result = [[]]
        self.path = []
    
    def subsetsWithDup(self, nums):
        self.used = [0 for i in range(len(nums))]
        nums.sort()
        self.trackbacking(nums, 0)
        return self.result

    def trackbacking(self, nums, stIndex):
        if self.path:
            self.result.append(self.path[:])

        for i in range(stIndex, len(nums)):
            self.path.append(nums[i])
            self.trackbacking(nums, i+1)
            self.path.pop()

要做剪枝,就需要在trackbacking函数中进行剪枝操作。我们查看上面的图,可以发现,[1, 2a, 2b]的情况是出现在纵向遍历中的,[2a, 3]和[2b, 3]的情况是出现在横向遍历中的(很重要,要理解)。

先上剪枝代码,再解释:

        for i in range(stIndex, len(nums)):
            if i > stIndex and nums[i] == nums[i-1]: #  剪枝
                continue
            self.path.append(nums[i])
            self.trackbacking(nums, i+1)
            self.path.pop()

nums[i] == nuims[i-1]好理解,意思是当前的选值和前面的重复了。这个条件在纵向和横向都会有满足的时候,如[1, 2a, 2b]在执行到加入元素2b时满足该情况(但此时不需要去重),[1, 2b]在执行到加入元素2b时,也满足(此时需要去重,因为会枚举出[1, 2b, 3],这与上一轮的[1, 2a, 3]是重复的)。

那么可以给nums[i] == nuims[i-1]增加条件来使它满足这个情况。

  • 纵向遍历时,可以发现每个递归条件都是(startIndex, len(nums)),每次都会从startIndex开始循环,当加入2a和2b时,循环里的i都是取值到startIndex。
  • 横向遍历时,即加入2b时,循环体中的i为startIndex+1,即 不是刚进入循环的时候了。
  • 因此,加入条件 i > stIndex ,即可满足情况!

全部代码如下:

class Solution(object):
    def __init__(self):
        self.result = [[]]
        self.path = []
        self.used = []
    
    def subsetsWithDup(self, nums):
        self.used = [0 for i in range(len(nums))]
        nums.sort()
        self.trackbacking(nums, 0)
        return self.result

    def trackbacking(self, nums, stIndex):
        if self.path:
            self.result.append(self.path[:])

        for i in range(stIndex, len(nums)):
            if i > stIndex and nums[i] == nums[i-1]:
                continue
            self.path.append(nums[i])
            self.trackbacking(nums, i+1)
            self.path.pop()

这与使用used数组的方法不同,也占用的空间更少。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值