47. 全排列 II

题目描述

给定一个可包含重复数字的序列 nums按任意顺序 返回所有不重复的全排列。

输入输出

1
2
3
4
5
6
7
8
输入:nums = [1,1,2]
输出:
[[1,1,2],
[1,2,1],
[2,1,1]]

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

大致思路

46. 全排列的基础上增加了序列中的元素可重复 这一条件,但要求:返回的结果又不能有重复元素。

思路是:在遍历的过程中,一边遍历一遍检测,在一定会产生重复结果集的地方剪枝

如果要比较两个列表是否一样,一个容易想到的办法是对列表分别排序,然后逐个比对。既然要排序,我们就可以 在搜索之前就对候选数组排序,一旦发现某个分支搜索下去可能搜索到重复的元素就停止搜索,这样结果集中不会包含重复列表。

代码实现上增加了

1
2
3
if (i > 0 && nums[i] == nums[i - 1] && used[i - 1]) {
continue;
}

KeyPoint: 代码中used[i - 1]!used[i - 1]都能通过 原因在于这里两种都可以达到去重的效果。每个数字都会经历填写或不填写两种情况。!used[i - 1]是每一次之前没填写过的跳过,used[i - 1]是之前填写了则跳过。都可以保证重复的只填一次。两种分别输出索引,可以看到索引的结果是不同的,但是索引上对应数字相同,所以结果都正确。

java实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution {
public List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> res = new ArrayList<List<Integer>>();
List<Integer> path = new ArrayList<Integer>();
boolean[] used = new boolean[nums.length];
Arrays.sort(nums);
backtrace(nums, res, 0, path, used);
return res;
}
public void backtrace(int[] nums, List<List<Integer>> res, int idx,
List<Integer> path, boolean[] used) {
if(idx == nums.length){
res.add(new ArrayList<Integer>(path));
return;
}
for(int i = 0; i < nums.length; i++){
if(used[i] || (i > 0 && nums[i] == nums[i - 1] && used[i - 1])){
continue;
}
path.add(nums[i]);
used[i] = true;
backtrace(nums, res, idx + 1, path, used);
used[i] = false;
path.remove(path.size() - 1);
}
}
}