classSolution { private List<Integer> getList(int[] nums) { List<Integer> result = newLinkedList<>(); for (inti=0; i < nums.length; i++) { result.add(nums[i]); } return result; } privatevoidswap(int[] nums, int i, int j) { inttemp= nums[i]; nums[i] = nums[j]; nums[j] = temp; } privatevoidpermuteUnique(int[] nums, List<List<Integer>> result, int startIndex, HashSet<String> set) { if (startIndex == nums.length - 1) { if (!set.contains(Arrays.toString(nums))) { set.add(Arrays.toString(nums)); result.add(getList(nums)); } return; } permuteUnique(nums, result, startIndex + 1, set); for (inti= startIndex + 1; i < nums.length; i++) { swap(nums, startIndex, i); permuteUnique(nums, result, startIndex + 1, set); swap(nums, startIndex, i); } } public List<List<Integer>> permuteUnique(int[] nums) { List<List<Integer>> result = newLinkedList<List<Integer>>(); if (nums == null || nums.length == 0) { result.add(newLinkedList<Integer>()); return result; } HashSet<String> set = newHashSet<>(); permuteUnique(nums, result, 0, set); return result; } }
剪枝查找
暴力查找虽然能解法问题,但是效率太低了。问题在于对于重复数字,在第一次跟 i 元素交换后,其产生了一系列的组合,但是在后续遇到重复数字时,其仍会跟 i 进行交换,导致产生了一系列的重复组合。例如:对于[2,1,1],在第一位数字2和第二位数字1交换后产生[1,2,1],然后递归产生[1,1,2],回溯后变回原先的组合[2,1,1],此时如果将第一位数字2和第三为数字1交换,则会产生重复的组合[1,1,2]。因此在每次交换前先判断要交换的数字是否相同或之前已经交换过,如果交换的两个数字相同或者要交换的数字之前已经交换过,则不进行交换(因为这样会产生重复的组合),否则进行交换然后递归回溯。代码如下