Given a collection of numbers that might contain duplicates, return all possible unique permutations.
Example:
Input: [1,1,2] Output: [ [1,1,2], [1,2,1], [2,1,1] ]
这道题现在的水平只考虑学会用回溯来解, 当时对于回溯这个思路是很不清晰的 什么时候要传入一个start索引, 什么时候不用传。
现在清晰明白了。
根据这道题来总结回溯解题的思路, 首先就是要根据给定的数组来形成一个排列列表, 列表中的每个排列都是独一无二的。
那么1,1, 2 三个元素就需要分别当做第一个元素来进行排列。 所以在回溯当中的for循环我门不需要传start索引,三个元素当中的元素放入了列表后就对数组从0开始遍历将其他元素也跟着放进去即可。 放进去的时候要注意一个问题, 就是不能重复放入。
那么就得理解它为什么会重复, 什么时候会重复。 根据上面提的回溯思路 每个元素放进去后要对数组又完整的遍历一遍(包括已经放进去的元素), 而且数组本身又有值相等的元素。 那么什么时候会重复呢 举个栗子: 1(假设为A1),1(假设B1),2, 就是A1 B1 2, 这样以后又有B1 A1 2 又或者A1 A1 2, B1 B1 2这样4种重复的情况。 这样列举出来代码就知道怎么写了。
class Solution {
public List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> list = new ArrayList<>();
Arrays.sort(nums);
backtrack(list, new ArrayList<>(), nums, new boolean[nums.length]);
return list;
}
private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, boolean [] used){
if(tempList.size() == nums.length) {
list.add(new ArrayList<>(tempList));
}
for(int i = 0; i < nums.length; i++) {
// used[i]是防止 A1 A1 2 B1 B1 2这两种重复情况, nums[i] == nums[i-1] && !used[i-1]是防止B1 A1 2
if(used[i] || i > 0 && nums[i] == nums[i-1] && !used[i - 1]) continue;
tempList.add(nums[i]);
used[i] = true;
backtrack(list, tempList, nums, used);
used[i] = false;
tempList.remove(tempList.size() - 1);
}
}
}