题目:给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。说明:解集不能包含重复的子集。
输入:[1,2,2] 输出:[[2],[1],[1,2,2],[2,2],[1,2],[]]
来源:https://leetcode-cn.com/problems/subsets-ii/
法一:自己的代码 时间超过百分之98
思路:通过画图观察出剪枝条件,如果上一个是一个节点与上一个数值相等,且上一个是入栈,下一个是出栈,则需要剪掉,排序后先对相同的数字进行判断可以节省更多的时间.

from typing import List
from collections import Counter
class Solution:
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
results = []
l = len(nums)
# 法一
# 将数字按出现频率由高到低排序,目的是提前剪枝来减少剪枝的次数,
# nums = [item for items, c in Counter(nums).most_common() for item in [items] * c]
# 这样时间会更短些,如果没有重复元素直接排序,如果有在按频率排序
# 法二
if l == len(set(nums)):
nums.sort()
else:
nums = [item for items, c in Counter(nums).most_common() for item in [items] * c]
def backtrack(a=[], nums=nums,count=0,sign=0):
if count == l:
results.append(a[:])
print(results)
return
for i in [nums[count]]:
count += 1
# 通过画图可以观察出,当两个数相同时,如果上一个节点是入栈,下一个节点是出栈,则该枝必须剪掉,
# 这个剪枝条件等价于如果上一个节点是出栈,下一个是入栈,二者是充要条件,
# sign用于标记是出栈还是入栈
# if条件用于判别是否执行剪枝
if (count>1) & (nums[count-1] == nums[count-2]) & (sign==0):
a.append(i)
sign = 0
backtrack(a,count=count,sign=sign)
# 以后关于栈的问题,压栈后务必要记得出栈,否则很容易出错
a.pop()
# 后面的不执行的即剪掉的枝
else:
a.append(i)
sign = 0
backtrack(a,count=count,sign=sign)
a.pop()
sign = 1
backtrack(a,count=count,sign=sign)
backtrack()
return results
if __name__ == '__main__':
duixiang = Solution()
a = duixiang.subsetsWithDup([1, 2,1, 2])
print(a)
