dcddc

西米大人的博客

0%

带重复元素的排列

题目描述

给出一个具有重复数字的列表,找出列表所有不同的排列。

思路

参考不带重复元素的全排列,讨论那四种方法是否适合带重复元素的全排列。

  • 穷举法
    因为包含重复元素,穷举法显然不合适。

  • 交换法
    对于不带重复元素的全排列,交换法是最合适的。但如果包含重复元素,交换的过程中会出现许多重复元素之间的交换,这种交换显然是错误的,所以交换法不合适。

  • 逆序阶乘进制法
    直接不能用!因为对于包含重复元素的n个元素的数组,其全排列不会有n!种情况。

    其实,只有后继法最合适!

  • 后继法
    后继法首先将数组按照升序排列,之后找当前数的下一个后继,每种后继都对应一种排列。直到最后找不到后继,完成全排列。
    当出现重复元素时,不但不会影响原先的求后继数算法,而且还会减少求后继数的次数(因为有重复元素),所以后继法本身就最合适于带重复元素的全排列!

    个人认为,不带重复元素的全排列,最适合用逆序阶乘进制法,交换法因为用递归所以稍逊

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
private List<List<Integer>> listHolder;

public List<List<Integer>> permuteUnique(int[] nums) {
listHolder = new ArrayList<List<Integer>>();
if(nums.length == 0) {
listHolder.add(new ArrayList<Integer>());
return listHolder;
}
Arrays.sort(nums);
addToListHolder(nums);
myPermuteUnique(nums);
return listHolder;
}

private void addToListHolder(int[] nums) {
Integer[] integers = new Integer[nums.length];
for(int i = 0; i < nums.length; i++) {
integers[i] = nums[i];
}
listHolder.add(new ArrayList<Integer>(Arrays.asList(integers)));
}

private void myPermuteUnique(int[] nums) {
int maxIndex;
int switchIndex;
// 如果存在极大值,寻找排列
while((maxIndex = getMaxIndex(nums)) != -1) {
int beforeMax = nums[maxIndex-1];
switchIndex = maxIndex;
// 寻找可交换元素下标
for(int i = maxIndex+1; i < nums.length; i++) {
if(nums[i] > beforeMax) {
switchIndex = i;
}else {
break;
}
}
// 交换元素
swap(maxIndex-1, switchIndex, nums);
// 从MaxIndex开始是降序排列,需要倒序得到后继数
int i = maxIndex;
int j = nums.length-1;
while(i < j) {
swap(i, j, nums);
i++;
j--;
}
// 找到一种排列,添加到List
addToListHolder(nums);
}
// 不存在MAX时,表示找到了所有排序
}

private void swap(int i, int j, int[] nums) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}

private int getMaxIndex(int[] nums) {
int i = nums.length-1;
while(i > 0) {
if(nums[i] > nums[i-1]) {
return i;
}
i--;
}
return -1;
}

考差点

  • 全排列