题目链接
解题思路:
参考
首先进行排序;
如果数组中没有重复的数字,就按照permutations的方法来做就可以了;
如果数组中有2个重复的数字,比如[1,1,3],首先将数组分为[1]和[1,3],
对[1,3]进行全排列有[1,3]和[3,1],在所有子全排列中的对应重复数字的后面添加数字即可,
比如我还需要添加1,第一个子全排列[1,3]的对应重复数字为1,1在第0位,所以我们在第1、2位分别再新添加进1,可以得到[1,1,3]和[1,3,1],
第二个子全排列[3,1]的对应重复数字1在第1位,所以我们只能在第二位添加进新的1,得到[3,1,1];
由此得到所有的全排列组合[1,1,3] [1,3,1]和[3,1,1];
如果有三个重复的数字,比如[1,1,1,3],首先将数组分为[1]和[1,1,3],通过递归可以得到[1,1,3]的全排列[1,1,3] [1,3,1] [3,1,1],
第一个全排列[1,1,3]的最后一个重复数字1在第1位(从0开始计),然后我们就在第2、3位添加新的1,得到[1,1,1,3]和[1,1,3,1]
第二个全排列[1,3,1]的最后一个重复数字1在最后一位,所以我们只能在最后一位的后面添加进新的1,得到[1,3,1,1]
对[3,1,1]进行相同的处理,可以得到[3,1,1,1]
这样就可以得到最终的所有结果了[1,1,1,3]、[1,1,3,1]、[1,3,1,1] 、 [3,1,1,1]
提交代码:
class Solution {
public List<List<Integer>> permuteUnique(int[] nums) {
//DP
List<List<Integer>> lists=new ArrayList<List<Integer>>();
if(nums==null||nums.length==0) return lists;
if(nums.length==1) {
List<Integer> list=new ArrayList<Integer>();
list.add(nums[0]);
lists.add(list);
return lists;
}
//merge sort
mergeSort(nums,0,nums.length-1);
//build subnums and calculate sublists
int subnums[]=new int[nums.length-1];
for(int i=1,j=0;i<nums.length;i++,j++)
subnums[j]=nums[i];
List<List<Integer>> sublists=permuteUnique(subnums);
//create current lists
for(int i=0;i<sublists.size();i++) { //子排列的个数
if(nums[0]==nums[1]) {
int startpos=0,flag=nums[1];
for(int pos=sublists.get(i).size()-1;pos>=0;pos--)
if(sublists.get(i).get(pos)==flag){
startpos=pos+1;
break;}
while(startpos<nums.length) {
List<Integer> list=new ArrayList<Integer>();
for(int j=0;j<sublists.get(i).size();j++)
//list=sublists.get(i); //不能直接get,不然对list的改动也会变更到sublists中去
list.add(sublists.get(i).get(j));
list.add(startpos, nums[0]);
lists.add(list);
startpos++;
}
}
else {
for(int pos=0;pos<nums.length;pos++) {
List<Integer> list=new ArrayList<Integer>();
//copy the i'th sublist to list
for(int j=0;j<sublists.get(i).size();j++)
list.add(sublists.get(i).get(j));
//list=sublists.get(i);
list.add(pos,nums[0]);
lists.add(list);
}
}
}
return lists;
}
public void mergeSort(int nums[],int left,int right) {
if(left>=right) return;
int mid=(left+right)/2;
mergeSort(nums,left,mid);
mergeSort(nums,mid+1,right);
merge(nums,left,mid,right);
}
public void merge(int nums[],int left,int mid,int right) {
if(left==right) return;
int len=right-left+1;
int[] tmp=new int[len];
int i=left,j=mid+1,k=0;
while(i<=mid&&j<=right)
tmp[k++]=nums[i]<nums[j]?nums[i++]:nums[j++];
while(i<=mid)
tmp[k++]=nums[i++];
while(j<=right)
tmp[k++]=nums[j++];
for(k=0;k<len;k++)
nums[left++]=tmp[k];
}
}