字典序法指对给定的字符集中的字符规定了一个先后关系,在此基础上规定两个全排列的先后是从左到右逐个比较对应的字符的先后。因此对于一个给定的序列,我们只要不断求出其下一个排列,直到其没有下一个(没有任何一个元素后面存在“应该在它前面”的元素),便可以逐一列举出当前序列的全部排列。
下面给出求任一排列 p=p(1)…,p(i-1),p(i),…,p(n) 的下一个排列的一般步骤:
- 找到当前排列的最后一个正序 a = max{ i | p(i) < p(i+1) } , 如果不存在说明当前排列即最后一个排列
- 找到最后大于 p(i) 者 b = max{ j | p(i) < p(j) }
- 互换 p(a) 与 p(b) 得 p(1),…,p(a-1),p(b),p(a+1),…,p(b-1),p(a),p(b+1),…,p(n)
- 反排位置 a 后面的数得到结果
例如:求数列 {2,5,9,7,3} 的下一个排列。
- 找到最后一个正序 {5,9},则 a = 2 , p(a) = 5
- 找到最后大于 p(2) = 5 的数,b = 4 , p(b) =7
- 交换 p(2) = 5 和 p(4) = 7 的位置得 {2,7,9,5,3}
- 反排位置 2 即 p(2) =7 后面 {9,5,3} 得结果 {2,7,3,5,9}
下面给出算法流程的 Java 及 Go 语言实现,讲解以 Java 为例
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
| public static int[] nextPermutation(int[] array) { int lastPositive = findLastPositive(array); if (lastPositive == -1) { return null; } int[] next = new int[array.length]; System.arraycopy(array, 0, next, 0, lastPositive);
int lastLarge = findLastLarge(array, lastPositive);
next[lastPositive] = array[lastLarge]; for (int i = lastPositive + 1; i < next.length; i++) { if (i == array.length + lastPositive - lastLarge) { next[i] = array[lastPositive]; } else { next[i] = array[array.length + lastPositive - i]; } } return next; }
private static int findLastPositive(int[] array) { for (int i = array.length - 2; i >= 0; i--) { if (array[i] < array[i + 1]) { return i; } } return -1; }
private static int findLastLarge(int[] array, int fromIndex) { for (int i = array.length - 1; i > fromIndex; i--) { if (array[i] > array[fromIndex]) { return i; } } return -1; }
|
** 源码位置 **