import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param num int整型一维数组
* @return int整型ArrayList<ArrayList<>>
*/
public ArrayList<ArrayList<Integer>> permuteUnique (int[] num) {
// write code here
// 左程云老师讲过全排列的分支限界递归法解决不重复全排列问题
// 调用一个递归函数,这个递归函数负责对当前i位置的数字进行与i ~ num.length - 1中的位置依次互换,然后继续考察i + 1处的情况
// 分支界限法去重:定义一个HashSet,这个位置没有出现过该值才允许进入分支,否则跳过
// 算法的时间复杂度0(N!),额外空间复杂度0(N!)
// 用于记录所有路径的结果
ArrayList<ArrayList<Integer>> finalResults = new ArrayList<>();
// 调用这个递归函数,从num数组的0位置开始,将每一次找到的oneResult填进finalResult中
process(num, finalResults, 0);
// 给finalResults中的元素按照字典序升序排列
finalResults.sort((o1, o2) -> {
// 遍历两个list的每个元素,找到第一对不相同的,按照升序返回
int i = 0, j = 0;
while (i < o1.size() && j < o2.size()) {
if (o1.get(i) != o2.get(j)) {
return o1.get(i) - o2.get(j);
}
i++;
j++;
}
return 0;
});
// 返回最终的答案finalResults
return finalResults;
}
public void process(int[] num, ArrayList<ArrayList<Integer>> finalResults,
int i) {
// 递归出口(底部)
if (i == num.length) {
// i越界了,说明整个数组遍历完毕了,将单个路径上的答案oneResult写入总答案finalResults中
// 踩坑点1:这里一定要【现场新创建】一个ArrayList放入答案中,如果提前在主方法创建好,那么随着每次递归的调用,finalResults里面的值也会被修改
ArrayList<Integer> oneResult = new ArrayList<>();
for (Integer n : num) {
oneResult.add(n);
}
finalResults.add(oneResult);
}
// 递归分支
HashSet<Integer> set = new HashSet<>();
for (int j = i; j < num.length; j++) {
if (!set.contains(num[j])) {
// 将num[j]登记
set.add(num[j]);
// 假设本位置与j位置的元素交换
swap(num, i, j);
// 在交换完的基础上继续考察num数组中i+1往后的位置该如何排列
process(num, finalResults, i + 1);
// 注意!恢复现场,继续尝试将本位值与num[j + 1]位置的元素交换
// 踩坑点2:一定要【恢复现场】,要不然无法准确列出要分支的情况
swap(num, j, i);
}
}
}
// 数组元素交换函数
public void swap(int[] num, int i, int j) {
if (i == j) {
return;
}
int t = num[i];
num[i] = num[j];
num[j] = t;
}
}