很久没更新了,主要是刷的简单题也没必要放在博客上。博主已经默默连续刷题快一个月了。今天遇到了个跟《代码随想录》题解不一样的思路、且更有效率,分享给大家~
题目
给你一个整数数组 nums
,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
思路
重点在于如何 去重 。如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数组的方法不同,也占用的空间更少。