这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战
前言
力扣第四十七题 全排列 II
如下所示:
给定一个可包含重复数字的序列 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]]
一、思路
这一题和 力扣第四十六题-全排列 基本是一样的,唯一的区别就是:数组中可能会有重复元素,需要返回所有不重复的全排列
那如何保证返回的结果中不会有重复值呢?主要有以下两点点:
- 在当前层的递归中,不可以选择相同的元素:使用排序 + 防止同层递归中加入相邻的相等元素来避免
- 相同的下标不能选择两次:下一层递归中,不能选择已选择过的元素
比如
nums = [1,1,2]
,首次递归已经选择过了第一个元素nums[0]
。等到再次回溯回来进行下一次迭代时,就不应该再选择nums[1] = 1
了,这样就能防止结果重复
举个例子
此处以示例 1中的 nums = [2,1,1]
作为例子
- 先将数组
升序
,nums = {1, 1, 2}
i=0
,nums[0] = 1
入栈,栈中的值为1
,并继续下一层递归- 递归1:因
i = 0
已选择过,跳过 - 递归1:
i=1
,nums[1] = 1
入栈,栈中的值为1 -> 1
,并继续下一层递归 - 递归2:因
i = 0
和i = 1
已选择过,跳过 - 递归2:
i=2
,nums[2] = 2
入栈,栈中的值为1 -> 1 -> 2
,此时栈满
,记录这一组结果,并将栈顶出栈,栈中的值为1 -> 1
。向上回溯 - 递归1:先将栈顶出栈,栈中的值为
1
。i=2
,nums[2] = 2
入栈,栈中的值为1 -> 2
,并继续下一层递归 - 递归2:因
i = 0
和i = 2
已选择过,跳过 - 递归2:
i=1
,nums[1] = 1
入栈,栈中的值为1 -> 2 -> 1
,此时栈满
,记录这一组结果,并将栈顶出栈,栈中的值为1 -> 2
。向上回溯 - 递归1:先将栈顶出栈,栈中的值为
1
。当前层遍历结束向上回溯 - 先将栈顶出栈,栈为空。因
i=1
时,nums[1] == nums[0]
,故跳过。(同层相邻元素相等) - ......,后续过程基本一致
- 最终可以获得正确的结果集:
[[1,1,2],[1,2,1],[2,1,1]]
二、实现
实现代码
实现代码与思路保持一致,其中使用 HashMap
来保存路径中的下标
/**
* 递归
*/
public List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> ret = new ArrayList<>();
Stack<Integer> stack = new Stack<>();
Arrays.sort(nums); // 升序排列
dfs(ret, nums, stack, new HashMap<>());
return ret;
}
public void dfs(List<List<Integer>> list, int[] nums, Stack<Integer> stack, Map<Integer, Object> map) {
if (stack.size() == nums.length) {
list.add(new ArrayList<>(stack));
return;
}
for (int i=0; i<nums.length; i++) {
int num = nums[i];
if (map.containsKey(i) || (i !=0 && num == nums[i-1] && map.containsKey(i-1)))
continue;
stack.push(num);
map.put(i, num);
dfs(list, nums, stack, map);
stack.pop();
map.remove(i);
}
}
测试代码
public static void main(String[] args) {
int[] nums = {2, 1, 1};
new Number47().permuteUnique(nums);
}
结果
优化版本
实现代码
使用全部变量 布尔数组
来替换 HashMap
,可以有效的提升效率
boolean[] vis;
public List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> ans = new ArrayList<List<Integer>>();
List<Integer> perm = new ArrayList<Integer>();
vis = new boolean[nums.length];
Arrays.sort(nums);
backtrack(nums, ans, 0, perm);
return ans;
}
public void backtrack(int[] nums, List<List<Integer>> ans, int idx, List<Integer> perm) {
if (idx == nums.length) {
ans.add(new ArrayList<Integer>(perm));
return;
}
for (int i = 0; i < nums.length; ++i) {
if (vis[i] || (i > 0 && nums[i] == nums[i - 1] && !vis[i - 1])) {
continue;
}
perm.add(nums[i]);
vis[i] = true;
backtrack(nums, ans, idx + 1, perm);
vis[i] = false;
perm.remove(idx);
}
}
结果
三、总结
感谢看到最后,非常荣幸能够帮助到你~♥