题目描述(中等难度)
给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
和上一道题类似,不同之处就是给定的数字中会有重复的,这样的话用之前的算法会产出重复的序列。例如,[ 1 1 ],用之前的算法,产生的结果肯定是 [ [ 1 1 ], [ 1 1 ] ],也就是产生了重复的序列。但我们可以在上一题的解法中进行修改从而解决这道题。
这次我们还是采用之前的回溯法。
之前的算法是这样的。
class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> list = new ArrayList<>();
List<Integer> tempList = new ArrayList<>();
backtrace(list,tempList,nums);
return list;
}
private void backtrace(List<List<Integer>> list,List<Integer> tempList,int[] nums){
if(tempList.size() == nums.length){
list.add(new ArrayList<>(tempList));
}else{
for(int i =0;i<nums.length;i++){
if(tempList.contains(nums[i])) continue;
tempList.add(nums[i]);
backtrace(list,tempList,nums);
tempList.remove(tempList.size() - 1);
}
}
}
}
第一个要解决的就是这句代码
if(tempList.contains(nums[i])) continue; // 已经存在的元素,跳过
之前没有重复的元素,所以可以直接在 templist
判断有没有当前元素,有的话就跳过。但这里的话,因为给定的有重复的元素,这个方法明显不可以了。
换个思路,我们可以再用一个 list
保存当前 templist
中已经有的元素的下标,然后添加新元素的时候去判断下标就可以了。
第二个问题就是,可以看到有重复元素的时候,上边第 1 个图和第 2 个图产生的是完全一样的序列。所以第 2 个遍历是没有必要的。
解决的方案就是把数组首先排下顺序,然后判断一下上一个添加的元素和当前元素是不是相等,相等的话就跳过,继续下一个元素。
Java
class Solution {
public List<List<Integer>> permuteUnique(int[] nums){
List<List<Integer>> list = new ArrayList<>();
List<Integer> templist = new ArrayList<>();
Arrays.sort(nums);
List<Integer> old = new ArrayList<>();
backtrack(list, templist, nums, old);
return list;
}
public void backtrack(List<List<Integer>> list, List<Integer> tempList, int[] nums, List<Integer> old) {
if(tempList.size() == nums.length){
list.add(new ArrayList<>(tempList));
}else{
for (int i = 0; i < nums.length; i++) {
if (old.contains(i)) continue;
if (i > 0 && !old.contains(i - 1) && nums[i - 1] == nums[i]) continue;
old.add(i);//添加下标
tempList.add(nums[i]); // 将当前元素加入
backtrack(list, tempList, nums, old); // 向后继续添加
old.remove(old.size() - 1);
tempList.remove(tempList.size() - 1);
}
}
}
public static void main(String args[]) {
int[] nums= {1,2,1};
List<List<Integer>> ans=permuteUnique(nums);
System.out.println(ans);
}
}