寻找全排列后的下一个数
-
问题:给出一个正整数,找出这个正整数所有数字全排列的下一个数。也就是这个数的数字全排列,找到一个比原数大的而且只比原数大的数。
-
例子:输入:12345 输出:12354
-
解法:
-
步骤:
- 从后往前查看逆序区域,得到逆序的前一位,就是数字置换的边界
- 让逆序区域的前一位和逆序区域中大于它的最小数字交换位置
- 把原来的逆序区域转化为顺序区域
-
代码:
public class FindNearestNumber {public static int[] findNearestNumber(int[] numbers){//1.从后向前查看逆序区域,找到逆序区域的第一位,也就是数字置换的边界int index = findTransferPoint(numbers);//如果数字置换边界是0,说明整个数组已经逆序,无法得到更大的相同数字组成的整数,返回nullif(index == 0){return null;}//2.把逆序区域的前一位和逆序区域中刚刚大于它的数字交换位置//拷贝入参,避免直接修改入参int[] numbersCopy = Arrays.copyOf(numbers, numbers.length);exchangeHead(numbersCopy, index);//3.把原来的逆序区域转为顺序reverse(numbersCopy, index);return numbersCopy;}private static int findTransferPoint(int[] numbers){// 从后往前查看,当前数字比前一个数大了,逆序区域结束,返回当前索引for(int i=numbers.length-1; i>0; i--){if(numbers[i] > numbers[i-1]){return i;}}return 0;}private static int[] exchangeHead(int[] numbers, int index){// 得到逆序区域的前一位,它要和逆序区域中大于它的最小元素交换int head = numbers[index-1];// 从后往前,遍历逆序区域,遍历到的第一个大于它的就是最小大于它的,就结束循环for(int i=numbers.length-1; i>0; i--){if(head < numbers[i]){numbers[index-1] = numbers[i];numbers[i] = head;break;}}return numbers;}// 把index后面的元素交换顺序private static int[] reverse(int[] num, int index){for(int i=index,j=num.length-1; i<j; i++,j--){int temp = num[i];num[i] = num[j];num[j] = temp;}return num;}public static void main(String[] args) {int[] numbers = {1,2,3,4,5};//打印12345之后的10个全排列整数。numbers一直在变化,找到更大的for(int i=0; i<10;i++){numbers = findNearestNumber(numbers);outputNumbers(numbers);}}//输出数组private static void outputNumbers(int[] numbers){for(int i : numbers){System.out.print(i);}System.out.println();} }
-
思考:三个步骤,每一步都是 O ( n ) O(n) O(n),因此时间复杂度是 O ( n ) O(n) O(n)
-