寻找全排列的下一个数(字典序算法实现)

匿名 (未验证) 提交于 2019-12-03 00:03:02
给出一个正整数,找出这个正整数所有数字全排列的下一个数。通俗的说就是在一个整数所包含数字的全部组合中,找到一个大于且仅大于原数的新整数。举例:
  • 如果输入:12345,则返回12354
  • 如果输入:12354,则返回12435
  • 如果输入:12435,则返回12453
思路:
字典序算法:
  1. 从后向前查看逆序区,找到逆序区域的前一位,作为数字置换的边界;
    1. 逆序区是指:数字大小(从左到右)排序一定会是从大到小。
    2. 所以从右往左找,第一个右边值大于左边值的位置,那么右边这个位置就是逆序区的起始点。
  2. 从右向左,从逆序区中找到大于【逆序区域起始点的前一位A】的最小的数字B,将A,B交换位置
    1. 因为逆序区的数字大小(从右到左)排序一定会是从小到大,所以只要从右向左找,第一个大于A的就一定也是逆序域中大于A的最小数字。
    2. 所以,从右向左遍历,找到第一个大于A的数字,就可以直接交换位置,退出循序。
  3. 把原来的逆序区域转为顺序状态;
    1. 同样的,因为逆序区的数字大小(从右到左)排序一定会是从小到大,所以只要从逆序区的两端,头尾值两两交换即可
举例: 1736542,逆序区域:6542,那么逆序区的起始点就是6,然后将3和4交换位置,变为:1746532,将逆序区域转为顺序状态:1742356。
代码实现:
 1 import java.util.Arrays;  2   3 /**  4  * 寻找全排列的下一个数  5  * 给出一个正整数,找出这个正整数所有数字全排列的下一个数。通俗的说就是在一个整数所包含数字的全部组合中,找到一个大于且仅大于原数的新整数。举例:  6  * 如果输入:12345,则返回12354  7  * 如果输入:12354,则返回12435  8  * 如果输入:12435,则返回12453  9  * 10  * 思路: 11  * 从后向前查看逆序区(数字从大到小排序),找到逆序区域的前一位,作为数字置换的边界; 12  * 让逆序区域的前一位和逆序区域中大于它的最小的数字交换位置; 13  * 把原来的逆序区域转为顺序状态; 14  * 举例: 12354,逆序区域:54,那么数字置换的边界就是3,将3和4交换位置,变为:12453,将逆序区域转为顺序状态:12435。 15  */ 16  17 public class FindNearestNumber { 18  19     public static void main(String[] args){ 20         int[] numArr = {1,7,3,6,5,4,2}; 21         int[] copyArr = Arrays.copyOf(numArr,numArr.length); 22         int[] result = new FindNearestNumber().findNearestNumber(copyArr); 23         System.out.println("原始的数组:" + Arrays.toString(numArr)); 24         System.out.println("处理后数组:" +Arrays.toString(result)); 25     } 26  27     public int[] findNearestNumber(int[] numArr){ 28         int borderIndex = getReverseBorderIndex(numArr); 29         int[] newNumArr = exchangePosition(numArr,borderIndex); 30         int[] result = changeReverse(newNumArr,borderIndex); 31         return result; 32     } 33  34  35     /** 36      * 从后向前,找到逆序区域边界点。 37      * 逆序区是指:数字大小(从左到右)排序一定会是从大到小。 38      * 所以从右往左找,第一个右边值大于左边值的位置,那么右边这个位置就是逆序区的起始点。 39      * @param numArr 40      * @return 41      */ 42     public int  getReverseBorderIndex(int[] numArr){ 43         int index = 0; 44         for (int i=numArr.length-1; i>0; i--){ 45             if (numArr[i] > numArr[i-1]){ 46                 return i; 47             } 48         } 49         return index; 50     } 51  52     /** 53      * 从右向左,从逆序区中找到大于【逆序区域起始点的前一位A】的最小的数字B,将A,B交换位置。 54      * 因为逆序区的数字大小(从右到左)排序一定会是从小到大,所以只要从右向左找,第一个大于A的就一定也是逆序域中大于A的最小数字。 55      * 所以,从右向左遍历,找到第一个大于A的数字,就可以直接交换位置,退出循序。 56      * @param numArr 57      * @return 58      */ 59     public int[] exchangePosition(int[] numArr, int borderIndex){ 60         int borderBeforeNum = numArr[borderIndex - 1]; 61         for (int i = numArr.length - 1; i > 0; i--) { 62             if (numArr[i] > borderBeforeNum) { 63                 numArr[borderIndex - 1] = numArr[i]; 64                 numArr[i] = borderBeforeNum; 65                 break; 66             } 67         } 68         return numArr; 69  70     } 71  72      /** 73      * 把原来的逆序区域转为顺序状态. 74       * 同样的,因为逆序区的数字大小(从右到左)排序一定会是从小到大,所以只要从逆序区的两端头尾两两交换即可 75      * @param numArr 76      * @param borderIndex 77      * @return 78      */ 79     public int[] changeReverse(int[] numArr, int borderIndex){ 80         for (int i = borderIndex, j = numArr.length - 1; i < j; i++, j--) { 81             int temp = numArr[i]; 82             numArr[i] = numArr[j]; 83             numArr[j] = temp; 84         } 85         return numArr; 86  87     } 88  89 }

原始的数组:[1, 7, 3, 6, 5, 4, 2] 处理后数组:[1, 7, 4, 2, 3, 5, 6]

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!