78题改进,出现了重复数字,需要添加限制条件。
之前一直在for架构下,在选择之前想办法做判断。
试了很多次都不成功,原来要修改的地方在“退出回溯 撤销选择”之后。
题目
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例
示例:
考察知识点
回溯、数组
核心思想
这道题和 Leecode 第78题 子集 差不多一样,区别是本题出现了重复数字,且结果不允许重复。78题的解题方法包括递归回溯、循环迭代、状态分配(基于字典、矩阵、位运算)三种方法。本题在78题代码的基础上添加某些限制条件即可。
方法一、递归回溯
相较于78题的区别是:
1、在调用 backtrack()
之前先将 nums
数组排序
2、当退出回溯函数 backtrack
并撤销选择 path.pop()
之后,判断后面是否有相同的数字,如果有,就跳过。
78题代码
| for i in range(start, n - (k - cur_len) + 1): path.append(nums[i]) backtrack(i + 1, path) path.pop()
|
90题代码
| nums.sort() i = first while i < n - (k - path_len) + 1: path.append(nums[i]) backtrack(i + 1, path, last) path.pop() while i + 1 < len(nums) and nums[i] == nums[i+1]: i += 1 i += 1
|
总结:之前一直在for架构下,在选择之前想办法做判断,试了很多次都不成功,原来要修改的地方在“退出回溯 撤销选择”之后。
方法二、循环迭代
相较于78题的区别是:
1、循环开始前先对 nums
进行排序
2、判断当前数字和前一个数字是否相似
2.1、如果和前一个数字一样,就在前一次循环产生的增量数组 tmp
上添加。
2.2、如果和前一个数字不一样,就在前一次循环产生的结果 output
上添加。
QQ图片20200320214359.jpg
78题代码
| for num in nums: output += [curr + [num] for curr in output] return output
|
90题代码
| nums.sort() tmp = [] for i in range(len(nums)): if i > 0 and nums[i - 1] == nums[i]: tmp = [curr + [nums[i]] for curr in tmp] else: tmp = [curr + [nums[i]] for curr in output] output += tmp return output
|
方法三、状态分配(字典排序(二进制排序) 子集)
暂无
Python版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| class Solution: def subsetsWithDup(self, nums: List[int]) -> List[List[int]]: def backtrack(first = 0, path = [], last=[]): path_len = len(path) if path_len == k: output.append(path[:]) return i = first while i < n - (k - path_len) + 1: path.append(nums[i]) backtrack(i + 1, path, last) path.pop() while i + 1 < len(nums) and nums[i] == nums[i+1]: i += 1 i += 1
nums.sort() output = [] n = len(nums) for k in range(n + 1): backtrack() return output
Input = [[1,1,2,2], [1,2,2]] Answer = [ [[],[1],[2],[1,1],[1,2],[2,2],[1,1,2],[1,2,2],[1,1,2,2],], [[], [1], [2], [1, 2], [2, 2], [1, 2, 2]] ]
if __name__ == "__main__": solution = Solution() for i in range(len(Input)): print("-"*50) reslut = solution.subsetsWithDup(Input[i]) if reslut == Answer[i]: print(True) else: print(False) print(reslut) print(Answer[i])
|
时间复杂度:\(O(N \times 2^N)\),考虑最坏的情况,不存在任何重复,就和78题一样了。
空间复杂度:\(O(N \times 2^N)\),同上,考虑最坏的情况,不存在任何重复,就和78题一样了。
执行用时 :48 ms, 在所有 Python3 提交中击败了42.99%的用户
内存消耗 :13.6 MB, 在所有 Python3 提交中击败了5.83%的用户
另一种简单回溯的写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| class Solution(object): def subsetsWithDup(self, nums): """ :type nums: List[int] :rtype: List[List[int]] """ if not nums: return [] n = len(nums) res = [] nums.sort() def backtrack1(idx, n, temp_list): if temp_list not in res: res.append(temp_list) for i in range(idx, n): backtrack1(i + 1, n, temp_list + [nums[i]]) def backtrack2(idx, n, temp_list): res.append(temp_list) for i in range(idx, n): if i > idx and nums[i] == nums[i - 1]: continue backtrack2(i + 1, n, temp_list + [nums[i]])
helper2(0, n, []) return res
|
时间复杂度:\(O(N \times 2^N)\)
空间复杂度:\(O(N \times 2^N)\)
执行用时 :32 ms, 在所有 Python3 提交中击败了97.05%的用户
内存消耗 :13.5 MB, 在所有 Python3 提交中击败了5.83%的用户
| class Solution: def subsetsWithDup(self, nums: List[int]) -> List[List[int]]: if not nums: return [] output = [[]] nums.sort() tmp = [] for i in range(len(nums)): if i > 0 and nums[i - 1] == nums[i]: tmp = [curr + [nums[i]] for curr in tmp] else: tmp = [curr + [nums[i]] for curr in output] output += tmp return output
|
时间复杂度:\(O(N \times 2^N)\) ,生成所有子集,并复制到输出结果中。
空间复杂度:\(O(N \times 2^N)\) ,这是子集的数量。
执行用时 :40 ms, 在所有 Python3 提交中击败了74.98%的用户
内存消耗 :13.6 MB, 在所有 Python3 提交中击败了5.83%的用户
参考链接
LeetCode用户 岳席文 题解
LeetCode用户 powcai 题解