给定一个可包含重复数字的序列,返回所有不重复的全排列。
示例:
输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
给定的序列中包含有重复数字,因此结果不能有重复的元素存在。一种方法就是使用上一道题的代码,只是在最后保存结果时多加一个判断。
class Solution:
def permuteUnique(self, nums: List[int]) -> List[List[int]]:
if nums == []: return []
results= []
def backtrack(nums, track, vis, depth= 0):
if depth== len(nums):
# 只有res中不包含和当前pre相同的元素时才保留
if track not in results:
results.append(track.copy())
return
for i in range(len((nums))):
if vis[i] == False:
track.append(nums[i])
vis[i] = True
backtrack(nums, track, vis, depth + 1)
vis[i] = False
track.pop()
nums = sorted(nums)
vis = [False] * len(nums)
backtrack(nums, [], vis, 0)
return results
但这样的方法多考虑了很多重复的情况,更好的方法是在递归的过程中进行剪枝。如果当前选择列表中的元素和上一次递归使用的元素相同,则使用它得到的结果必然是重复的,此时需跳过进行下一个元素的判断。
class Solution:
def permuteUnique(self, nums: List[int]) -> List[List[int]]:
results = []
l = len(nums)
def backtrack(nums, track, depth= 0):
# if nums == []表示当前选择列表为空,已到达叶子结点
if depth== l:
results .append(track.copy())
return
for i in range(len(nums)):
# 剪枝
if i > 0 and nums[i] == nums[i - 1]:
continue
track.append(nums[i])
backtrack(nums[:i] + nums[i + 1:], track, depth+ 1)
track.pop()
if nums == []: return []
nums = sorted(nums)
backtrack(nums, [], 0)
return results
2020 - 8 - 2 更新Java解题代码,如下所示:
/**
* @Author dyliang
* @Date 2020/8/2 0:14
* @Version 1.0
*/
class Solution {
public List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> results = new LinkedList<>();
LinkedList<Integer> track = new LinkedList<>();
// 先将数组中数字按需排列,便于后续对可能的重复情况进行剪枝
Arrays.sort(nums);
boolean[] vis = new boolean[nums.length];
dfs(nums, track, vis, 0, results);
results.forEach(System.out :: println);
return results;
}
private void dfs(int[] nums, LinkedList<Integer> track, boolean[] vis, int depth, List<List<Integer>> results) {
// 终止条件
if (depth == nums.length) {
results.add(new LinkedList<>(track));
return;
}
for (int i = 0; i < nums.length; i++) {
if(vis[i]){
continue;
}
// 如果当前索引不为0的情况下,当前选择的元素和上一个选择的相同,进行剪枝
if (i > 0 && nums[i] == nums[i - 1] && vis[i - 1] == false){
continue;
}
// 做出选择
track.add(nums[i]);
vis[i] = true;
dfs(nums, track, vis, depth + 1, results);
// 撤销选择
track.removeLast();
vis[i] = false;
}
}
}
在提交的过程中发现,如果去重的判断发生在最后保存可能路径的时候,虽然可以通过所有的测试样例,但是会出现时间超过的问题。显然判断当前可选择的元素是否和上一个选择的相同,相比于在路径列表中判断当前路径是否存在时间复杂度更低。