一、题设
给定一个可包含重复数字的序列 nums
,按任意顺序 返回所有不重复的全排列。
示例 1:
输入:nums = [1,1,2] 输出: [[1,1,2], [1,2,1], [2,1,1]]
示例 2:
输入:nums = [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
二、基本思路
遇到组合、切割、子集、排列、棋盘(八皇后、数独)问题用回溯法。回溯法的基本套路格式如下:
def backtracking(参数):
if 终止条件: # 达到叶子节点
收集数据
return
# 单层逻辑
for i in range(lens):#遍历数据集
处理节点
backtracking(参数)
撤销处理节点的操作
对于该题我们只需要确定:
1.函数的参数:生成的是不同的排列,则我们需要一个列表res存放最后的所有排列,用列表tmp存放当前的数据,用flag数组作为标记数组表示当前数是否取到的状态。故我们的回溯函数则有三个参数:(1)tmp,初始状态为[] (2)flag,初始状态为[ 0 for i in range(所给列表长度)] (3)nums,用于把每次的数组值nums[i]加到tmp中。(注:列表与int不能直接相加)
2.终止条件:当tmp中的元素个数与原数组个数相等时即为一次全排列。
3.处理节点:本题对于单层节点的处理可以分为以下三个步骤:3.1 去重:对已取到元素的排除;3.2 剪枝:剔除相同结果排列;3.3 对取到当前节点做标记。
3.1 去重:遍历全部元素集,当flag[i] == 1时,即已取过该元素,则跳过。这就是对已取到元素的排除。
3.2 剪枝:剔除相同结果排列。在开始对所给列表按值进行升/降序,使得相同的数相邻。当在某下标之前的且与当前下标元素值相同但当前没取过时,则跳过,即为剪枝。样例如下图所示:
3.3 对取到当前节点做标记:flag[i] = 1
4.backtracking(参数):(1) nums:不变 (2) tmp:加上准备放入的元素nums[i] (3) flag:不变,已修改
5.撤销处理节点的操作:将flag[i] 置为0即可,下次还可以被选择。
def permuteUnique(self, nums):
def backtracking(nums,tmp,flag):
if len(tmp) == len(nums):# 到叶子节点
res.append(tmp)# 保存结果
return
for i in range(len(nums)):# 遍历元素集
if flag[i] == 1:# 当该元素被取到了就跳过
continue
if i > 0 and nums[i] == nums[i-1] and flag[i-1] == 0:# 当
continue
flag[i] = 1 # 取当前元素,flag[i]置为1
backtracking(nums,tmp + [nums[i]],flag) # 递归
flag[i] = 0 # 回溯,flag[i]置为0不取当前数了
res = []
nums.sort()
flag = [0 for _ in range(len(nums))]
backtracking(nums,[],flag)
return res