给定一系列数字,可能会包含重复数字,返回所有可能的唯一的排列。
示例:
Input: [1,1,2]
Output:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
思路
本题与上一题 Leetcode算法——46、全排列 很相似,不同之处在于,上一题的元素都是无重复的,而本题的元素允许重复,但是要求返回无重复的排列。
上一题我们使用了 4 种方法来解决全排列问题,本题我们也用同样 4 种方法来解决,但是需要加入去重的操作。
1、字典序法
使用字典序法不断寻找当前排序的下一个排序,直至返回到了起始序列。
字典序法与是否有重复元素无关,因为他的结果是按照从小到大的顺序返回的,无论元素是否重复,返回的结果都不会重复。
因此使用字典序法不用做任何修改,同时适用于上一题和本题。
2、深度优先搜索
使用递归法,维护一个结果数组、以及每个元素对应的剩余元素集合。每次依次从剩余的元素集合中取出一个,并且分别追加到每一个已取序列的最后,直至剩余元素集合为空。
注意,如果当前取出的一个,在之前的取法中已经取出过同样的元素值(即深度树已经有了一个同样的分叉),则跳过,防止重复。
为了让相同的元素值只可能是相邻元素,我们需要提前对数组进行排序。
3、广度优先搜索
维护一个状态列表,里面的元素是所有状态词典,词典中有两个key,记录已经搜索到的和尚未搜索到的。
每次循环,对于所有状态词典,对每个尚未搜索到的数字,都拼接到已经搜索到的数组的末尾,然后形成一个新的状态列表。
这样,每次循环,所有状态词典中的已搜索到的序列长度+1,尚未搜索到的集合长度-1。
因此,只需要循环 len(nums) 次,所有状态都表示搜索完毕了。
同样地,如果当前取出的一个,在之前的取法中已经取出过同样的元素值(即广度树已经有了一个同样的分叉),则跳过,防止重复。
4、分治法
设nums的长度为n,则前n项的全排列 = 第n个数字依次插入到前n-1项的全排列的两两之间的缝隙中(包括首尾两端)。
但是注意,如果有重复元素,则可能有些缝隙的插入是重复的。比如数组为 [1,2,2],前 n-1 项的全排列为 [1,2] 和 [2,1]。以 [1,2] 为例,将第 n 项插入到缝隙中,那么插入到 1 和 2 之间,和插入到 2 的后面,结果都是 [1,2,2],是重复的,只能保留一个。
如果要避免重复,只需要判断插入到缝隙之后,新元素的左边是否和新元素相等。如果相等,则不再继续这个缝隙的插入以及之后所有缝隙的插入。注意是结束之后所有缝隙的插入,而不仅仅是跳过继续下一个缝隙。
python实现
def permuteUnique(nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
深度优先搜索。
"""
def get_rest(result_list, pre_list, rest_list):
'''
递归,对rest_list进行全排列,并分别与pre_list拼接,最终加入到result_list中
'''
if not rest_list:
result_list.extend(pre_list)
for i in range(len(rest_list)):
if i > 0 and rest_list[i] == rest_list[i-1]: # 防止重复
continue
new_list = rest_list[:i] + rest_list[i+1:] # 去掉第i个元素
get_rest(result_list, [x+[rest_list[i]] for x in pre_list], new_list)
nums_sort = sorted(nums) # 排序,便于判断是否重复
result_list = []
get_rest(result_list, [[]], nums_sort)
return result_list
def permuteUnique2(nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
广度优先搜索。
"""
nums_sort = sorted(nums)
state_list = [{'pre':[],'rest':nums_sort}]
for _ in range(len(nums)): # 循环len(nums)次
new_state_list = []
for dic in state_list:
rest_list = dic['rest']
for i in range(len(rest_list)):
if i > 0 and rest_list[i] == rest_list[i-1]: # 防止重复
continue
new_list = rest_list[:i] + rest_list[i+1:] # 去掉第i个元素
new_state_list.append({'pre':dic['pre']+[rest_list[i]],'rest':new_list})
state_list = new_state_list
return [x['pre'] for x in state_list]
def permuteUnique3(nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
分治法。
"""
def get_permutes(n):
'''
得到nums前n项的全排列。
'''
# 递归结束条件
if n == 1:
return [[nums[0]]]
# 得到前n-1项的全排列
pre_list = get_permutes(n-1)
# 将第 n 个元素,依次插入到 pre_list 中的元素缝隙中
result_list = []
for permute in pre_list:
for i in range(len(permute)+1):
if i > 0 and nums[n-1] == permute[i-1]: # 防止重复
# 不能是continue。一旦碰到相同数字,则后面所有空隙的插入都略过。
break
result_list.append(permute[:i] + [nums[n-1]] + permute[i:])
return result_list
nums.sort()
return get_permutes(len(nums))
if '__main__' == __name__:
nums = [1,1,2,2]
print(permuteUnique3(nums))