给出一个正整数,找出这个正整数所有数字全排列的下一个数。
如果输入 12345,则返回 12354。

如果输入 12354,则返回 12435。

如果输入 12435,则返回 12453。

例如给出整数 12354,它包含的数字是 1、2、3、4、5,如何找到这些数字全排列之后仅大于原数的新整数呢?

为了和原数接近,我们需要尽量保持高位不变,低位在最小的范围内变换顺序。

至于变换顺序的范围大小,则取决于当前整数的逆序区域。

image.png
如图所示,12354 的逆序区域是最后两位,仅看这两位已经是当前的最大组合。若想最接近原数,又比原数更大,必须从倒数第 3 位开始改变。

怎样改变呢?12345 的倒数第 3 位是 3,我们需要从后面的逆序区域中找到大于 3 的最小的数字,让其和 3 的位置进行互换。

image.png
互换后的临时结果是 12453,倒数第 3 位已经确定,这个时候最后两位仍然是逆序状态。我们需要把最后两位转变为顺序状态,以此保证在倒数第 3 位数值为 4 的情况下,后两位尽可能小。

image.png
这样一来,就得到了想要的结果 12435。

image.png

获得全排列下一个数的 3 个步骤,也称为字典序算法

  1. 从右往左,找出第一个左边小于右边的数
  2. 让逆序区域的前一位和逆序区域中大于它的最小的数字交换位置。
  3. 把原来的逆序区域转为顺序状态 。
  1. function findNearestNumber(numbers){
  2. //1.从后向前查看逆序区域,找到逆序区域的前一位,也就是数字置换的边界
  3. let index = findTransferPoint(numbers);
  4. //如果数字置换边界是0,说明整个数组已经逆序,无法得到更大的相同数
  5. //字组成的整数,返回null
  6. if(index == 0){
  7. return null;
  8. }
  9. let numbersCopy = [...numbers];
  10. //2.把逆序区域的前一位和逆序区域中刚刚大于它的数字交换位置
  11. exchangeHead(numbersCopy, index);
  12. //3.把原来的逆序区域转为顺序
  13. reverse(numbersCopy, index);
  14. return numbersCopy;
  15. }
  16. function findTransferPoint(numbers){
  17. // 1.从右往左,找出第一个左边小于右边的数,比如12354,那么i就是5,i后边的区域称为逆序区域
  18. for(let i=numbers.length-1; i>0; i--){
  19. if(numbers[i-1] < numbers[i]){
  20. return i;
  21. }
  22. }
  23. return 0;
  24. }
  25. function exchangeHead(numbers, index){
  26. let head = numbers[index-1];// i左边的数,比如12354,i是5,head就是3
  27. //2.从右往左,找出第一个大于i左边的数,并交换位置
  28. for(let i=numbers.length-1; i>0; i--){
  29. if(numbers[i] > head){
  30. numbers[index-1] = numbers[i];
  31. numbers[i] = head;
  32. break;
  33. }
  34. }
  35. return numbers;
  36. }
  37. function reverse(num, index){
  38. //3.i之后的从小到大排列
  39. num = [...num.slice(0, index), ...num.slice(index).sort((a, b) => a-b)]
  40. }