解法一:回溯

和普通的全排列一样,都可以使用回溯法,唯一的区别在于需要处理重复元素之前位置互换带来的冗余。令重复元素之前保持某种固定的顺序是一种解决冗余的思路。

  1. class Solution {
  2. private List<List<Integer>> ans;
  3. private boolean[] isVisited;
  4. public List<List<Integer>> permuteUnique(int[] nums) {
  5. int n = nums.length;
  6. ans = new LinkedList<>();
  7. isVisited = new boolean[n];
  8. // 排序使得重复元素连在一起
  9. Arrays.sort(nums);
  10. Integer[] arr = new Integer[n];
  11. permute(n, nums, 0, arr);
  12. return ans;
  13. }
  14. private void permute(final int N, final int[] nums, int index, Integer[] arr) {
  15. if (index == N) {
  16. List<Integer> list = new ArrayList(N);
  17. Collections.addAll(list, arr);
  18. ans.add(list);
  19. return;
  20. }
  21. for (int i = 0; i < N; ++i) {
  22. if (isVisited[i]) {
  23. continue;
  24. }
  25. // 对于重复数字, 在全排列中出现的次序必须与原来的顺序保持一致
  26. // 因此如果原来排在它前面的重复数字还没有被使用, 它也不能加入全排列
  27. if ((i > 0) && (nums[i] == nums[i - 1]) && (!isVisited[i - 1])) {
  28. continue;
  29. }
  30. arr[index] = nums[i];
  31. isVisited[i] = true;
  32. permute(N, nums, index + 1, arr);
  33. isVisited[i] = false;
  34. }
  35. }
  36. }