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

Posted zldmy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了寻找全排列的下一个数(字典序算法实现)相关的知识,希望对你有一定的参考价值。

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

 

 结果:

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

 

 

以上是关于寻找全排列的下一个数(字典序算法实现)的主要内容,如果未能解决你的问题,请参考以下文章

算法问题寻找全排列的下一个数

全排列的实现方法--递归&字典序

全排列 (递归求解+字典序) java 转载

[PHP]算法-拼接最小字典序的实现

全排列 字典序全排列

算法系列学习一全排列的生成算法