题目
给定一个可包含重复数字的序列,返回所有不重复的全排列。
示例:
输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
分析
该题目和 全排列I 的差别在于:本题目可以包含重复的数字,并且要求返回所有不重复的全排列。
按照常规的思路,我们画出深度优先搜索树,并确定路径和可选列表。
如 nums = [1, 2, 2], 它的深度优先搜索树如下:
最关键的是去重操作:
for (int i = 0; i < candidate.size(); i++) {
if (i != 0 && candidate.get(i) == candidate.get(i - 1)) continue;
int tmp = candidate.remove(i);
path.add(tmp);
permute(res, candidate, path);
path.remove(path.size() - 1);
candidate.add(i, tmp);
}
如:在根节点时,path = {}, candidate = {}, 在 i 指标进行循环时,如果遇到 candidate[i] == candidate[i - 1],那么进行剪枝,也就是说当 i = 2时,candidate[2] = (candidate[1] = ), 该枝应该被剪掉。(因为它的 path = {}, candidate = {1, }, 与前面的一枝是完全重复的)。该方法在 子集II 和 三数之和 中都有用到。
代码
class Solution {
public List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if (nums == null || nums.length == 0) return res;
Arrays.sort(nums);
List<Integer> candidate = new ArrayList<>();
for (int num : nums) candidate.add(num);
permute(res, candidate, new ArrayList<>());
return res;
}
private void permute(List<List<Integer>> res, List<Integer> candidate, List<Integer> path) {
if (candidate.size() == 0) {
res.add(new ArrayList<>(path));
return;
}
for (int i = 0; i < candidate.size(); i++) {
if (i != 0 && candidate.get(i) == candidate.get(i - 1)) continue;
int tmp = candidate.remove(i);
path.add(tmp);
permute(res, candidate, path);
path.remove(path.size() - 1);
candidate.add(i, tmp);
}
}
}