给定一个不重复数组组成的数组,比如{1,2,3},按照从小到大的顺序组成的全排列整数有6个:123、132、213、231、312、321,这6个数字都是换位数,即组成的数字一样,只是位置不一样而已。
一、最近最大换位数
首先解决第一个问题,如何找到给定整数,离它最近且比它大的换位数。比如:12534的最近最大换位数是12543,13254的最近最大换位数是13425。
为了和原数接近,一个原则是尽量保持高位不变,地位在最小范围内按顺序顺序变化。
以12354为例,逆序区域为54,54两位已经是当前两个数字的最大组合。要想接近原数并且比原数大,就要从倒数第三位开始改变。
12345的倒数第3位是3,我们需要从后面的逆序区域中寻找到刚刚大于3的数字,和3的位置进行互换:
12453
互换后的临时结果是12453,倒数第3位已经确定,这时候最后两位仍然是逆序状态。我们需要把最后两位转变回顺序,以此保证在倒数第3位数值为4的情况下,后两位尽可能小:
12435
获得最近换位数的三个步骤:
- 从后向前查看逆序区域,找到逆序区域的前一位,也就是数字置换的边界
- 把逆序区域的前一位和逆序区域中刚刚大于它的数字交换位置
- 把原来的逆序区域转为顺序
这样,每一步都是O(N),整个算法的时间复杂度为O(N).
代码:
private static int findTransferPoint(int[] nums) {
for (int i = nums.length - 1; i > 0; i--) {
if (nums[i] > nums[i - 1]) {
return i;
}
}
return 0;
}
private static int[] exchange(int[] nums, int index) {
int head = nums[index - 1];
for (int i = nums.length - 1; i > 0; i--) {
if (nums[i] > head) {
nums[index - 1] = nums[i];
nums[i] = head;
break;
}
}
return nums;
}
private static int[] reverse(int[] nums, int index) {
for (int i = index, j = nums.length - 1; i < j; i++, j--) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
return nums;
}
public static int[] findNearestNum(int[] nums) {
int[] numsCopy = Arrays.copyOf(nums, nums.length);
int index = findTransferPoint(numsCopy);
if (index == 0) {
return null;
}
exchange(numsCopy, index);
reverse(numsCopy, index);
return numsCopy;
}
测试:
public static void main(String[] args) {
int[] nums = {1,2,3,5,4};
System.out.println("before: "+Arrays.toString(nums));
findNearestNum(nums);
System.out.println("result: "+Arrays.toString(findNearestNum(nums)));
}
输出:
before: [1, 2, 3, 5, 4]
before: [1, 2, 4, 3, 5]
二、全排列
n个不相同的数字的全排列有n!个,写个函数求n的阶乘:
private static int factorrail(int n) {
if (n == 1) {
return 1;
} else {
return n * factorrail(n - 1);
}
}
利用找最近最大换位数的方法,把数字从小到大排序,找出全部的换位数:
public static void main(String[] args) {
int[] nums = {5, 1, 2, 3, 4};
Arrays.sort(nums);
int n = factorrail(nums.length);
System.out.println("1:" + Arrays.toString(nums));
for (int i = 2; i <= n; i++) {
nums = findNearestNum(nums);
System.out.println(i + ":" + Arrays.toString(nums));
}
}
输出:
1:[1, 2, 3, 4, 5]
2:[1, 2, 3, 5, 4]
3:[1, 2, 4, 3, 5]
4:[1, 2, 4, 5, 3]
5:[1, 2, 5, 3, 4]
6:[1, 2, 5, 4, 3]
...
117:[5, 4, 2, 1, 3]
118:[5, 4, 2, 3, 1]
119:[5, 4, 3, 1, 2]
120:[5, 4, 3, 2, 1]