给你一个整数数组
nums
,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
输入:nums = [1,2,2]
输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]输入:nums = [0]
输出:[[],[0]]
- 提示:
1 <= nums.length <= 10
-10 <= nums[i] <= 10
本题与Leetcode 78 子集的区别就是集合里有重复元素,且要求子集去重。
其实子集也是一种组合问题,因为它的集合是无序的,子集{1,2} 和 子集{2,1}是一样的。
举个栗子: nums = [1,2,2]为例把求子集抽象为树型结构,如下:
注: 去重需要先对集合排序
回溯三部曲:
回溯函数要处理的参数以及返回值
全局变量:result(保存子集集合,二维) path:子集收集元素 startIndex(控制下一层的起始位置)
result,path = [], []
def backtrack(nums:List[int], startIndex:int):
递归终止条件:
剩余集合为空时,即为叶子结点。
if (startIndex >= len(nums)):
return
其实可以不需要加终止条件,因为startIndex >= nums.size(),本层for循环本来也结束了。
单层搜索逻辑
nums[i] == nums[i - 1]
并且 used[i - 1] == False
,就说明:前一个树枝,使用了nums[i - 1],也就是说同一树层使用过nums[i - 1]。# 单层递归逻辑
for i in range(start_index, len(nums)):
if i > 0 and nums[i] == nums[i-1] and used[i-1]==False :
continue
path.append(nums[i])
used[i] = True
backtracking(nums, i+1)
used[i] = False
path.pop()
# 使用uesd数组
class Solution:
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
result, path = [], []
used = [False] * len(nums)
def backtrack(nums: List[int], startIndex: int) -> None:
result.append(path[:])
# 终止条件
if startIndex == len(nums):
return
for i in range(startIndex, len(nums)):
if i > 0 and nums[i - 1] == nums[i] and used[i - 1] == False:
continue
# 处理节点
path.append(nums[i])
used[i] = True
# 递归
backtrack(nums, i + 1)
# 回溯
used[i] = False
path.pop()
# 排序
nums.sort()
backtrack(nums, 0)
return result
# 不使用used数组
class Solution:
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
result, path = [], []
def backtrack(nums: List[int], startIndex: int) -> None:
result.append(path[:])
# 终止条件
if startIndex == len(nums):
return
for i in range(startIndex, len(nums)):
if i > startIndex and nums[i - 1] == nums[i]:
continue
# 处理节点
path.append(nums[i])
# 递归
backtrack(nums, i + 1)
# 回溯
path.pop()
# 排序
nums.sort()
backtrack(nums, 0)
return result
参考:
https://www.programmercarl.com/0090.%E5%AD%90%E9%9B%86II.html#%E5%85%B6%E4%BB%96%E8%AF%AD%E8%A8%80%E7%89%88%E6%9C%AC