【题目】
Given a collection of numbers that might contain duplicates, return all possible unique permutations.
For example,
[1,1,2]
have the following unique permutations:
[1,1,2]
, [1,2,1]
, and [2,1,1]
.
题意:求集合中所有数的全排列,注意集合中可能存在重复的数。
分析:方法与Permutations 相同,只是有了重复数后,全排列的总数就不足n!个了,我们的方法是先对所有数排序,即从最小的排列开始找,找到最后一个排列时结束。对122,第一个数1与第二个数2交换得到212,然后考虑第一个数1与第三个数2交换,此时由于第三个数等于第二个数,所以第一个数不再与第三个数交换。再考虑212,它的第二个数与第三个数交换可以得到解决221。此时全排列生成完毕。如果用交换法来解,需要定义一个set来存储已经交换过的元素值(好的利用)。
采用字典序的非递归方法。 从字典顺序最小的一种排列开始,每次获得字典序刚好比前一个排列大的排列,直到得到字典序最大的排列时,就得到了所有的结果, 以字符串"abc"为例,"abc"是字典序最小的排列,所有情况按字典序排列为"abc","acb","bac","bca","cba","cab"。
实现:
public ArrayList<ArrayList<Integer>> permute(int[] num) {
ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
permuteUnique(num, 0, result);
return result;
}
private void permuteUnique(int[] num, int start, ArrayList<ArrayList<Integer>> result) {
if (start >= num.length ) {
ArrayList<Integer> item = convertArrayToList(num);
result.add(item);
}
for (int j = start; j <= num.length-1; j++) {
if (containsDuplicate(num, start, j)) {
swap(num, start, j);
permuteUnique(num, start + 1, result);
swap(num, start, j);
}
}
}
private ArrayList<Integer> convertArrayToList(int[] num) {
ArrayList<Integer> item = new ArrayList<Integer>();
for (int h = 0; h < num.length; h++) {
item.add(num[h]);
}
return item;
}
private boolean containsDuplicate(int[] arr, int start, int end) {
for (int i = start; i <= end-1; i++) {
if (arr[i] == arr[end]) {
return false;
}
}
return true;
}
private void swap(int[] a, int i, int j) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
利用set:
public static ArrayList<ArrayList<Integer>> permuteUnique(int[] num) {
ArrayList<ArrayList<Integer>> returnList = new ArrayList<ArrayList<Integer>>();
returnList.add(new ArrayList<Integer>());
for (int i = 0; i < num.length; i++) {
Set<ArrayList<Integer>> currentSet = new HashSet<ArrayList<Integer>>();
for (List<Integer> l : returnList) {
for (int j = 0; j < l.size() + 1; j++) {
l.add(j, num[i]);
ArrayList<Integer> T = new ArrayList<Integer>(l);
l.remove(j);
currentSet.add(T);
}
}
returnList = new ArrayList<ArrayList<Integer>>(currentSet);
}
return returnList;
}
运用了递归与非递归的方法解决了全排列问题,总结一下就是:
1.全排列就是从第一个数字起每个数分别与它后面的数字交换。
2.去重的全排列就是从第一个数字起每个数分别与它后面非重复出现的数字交换。
3.全排列的非递归就是由后向前找替换数和替换点,然后由后向前找第一个比替换数大的数与替换数交换,最后颠倒替换点后的所有数据。