90子集II
题目:给定一个可能包含重复元素的整数数组 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)