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


    寻找全排列的下一个数

    摘自漫画算法:

    题目:给出一个正整数,找出这个正整数所有数字全排列的下一个树。说的通俗点就是在一个整数所包含数字的全部组合中,找到一个大于且仅大于原数的新整数。

    例子:

    • 如果输入12345,则返回12354
    • 如果输入12354,则返回12435
    • 如果输入12435,则返回12453

    解题思路

    在给出具体思路解法之前,先思考一个问题:由固定几个数字组成的整数,怎么排列最大?怎么排列最小?

    解答:如果是固定的几个数字,在逆序排列的情况下值最大,在顺序排列的情况下值最小

    例子:

    给出1、2、3、4、5这几个数字。最大组合为54321,最小组合为12345。

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

    为了和原数接近,我们需要尽量保持高位不变,低位在最小的范围内变换顺序。至于变换顺序的范围大小,则取决于当前整数的逆序区域。

    图1.png

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

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

    如图:

    图2.png

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

    图3.png

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

    总结:以上思路看起来复杂,其实只要3个步骤:

    • 从后向前查看逆序区域,找到逆序区域的前一位,也就是数字置换的边界。
    • 让逆序区域的前一位和逆序区域中大于它的最小数字交换位置。
    • 把原来的逆序区域转为顺序状态。

    这种解法有一个“高大上”的名字:字典序算法。

    代码实现

    import java.util.Arrays;
    
    /**
     * 描述:寻找全排列的下一个树。
     * <p>
     * Create By ZhangBiao
     * 2020/6/7
     */
    public class RangeNextNumber {
    
        public static int[] findNearestNumber(int[] numbers) {
            // 1、从后向前查看逆序区域,找到逆序区域的前一位,也就是数字置换的边界
            int index = findTransferPoint(numbers);
            // 如果数字置换边界是0,说明整个数组已经逆序,无法得到更大的相同数字组成整数,返回null
            if (index == 0) {
                return null;
            }
            // 2、把逆序区域的前一位和逆序区域中刚刚大于它的数字交换位置
            // 复制并入参,避免直接修改入参
            int[] numbersCopy = Arrays.copyOf(numbers, numbers.length);
            exchangeHead(numbersCopy, index);
            // 3、把原来的逆序区域转为顺序
            reverse(numbersCopy, index);
            return numbersCopy;
        }
    
        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;
        }
    
        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;
        }
    
        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 void outputNumbers(int[] numbers) {
            for (int i : numbers) {
                System.out.print(i);
            }
            System.out.println();
        }
    
        public static void main(String[] args) {
            int[] numbers = {1, 2, 3, 4, 5};
            // 打印12345之后的10个全排列整数
            for (int i = 0; i < 19; i++) {
                numbers = findNearestNumber(numbers);
                outputNumbers(numbers);
            }
        }
    
    }
    
  • 相关阅读:
    BestCoder17 1001.Chessboard(hdu 5100) 解题报告
    codeforces 485A.Factory 解题报告
    codeforces 485B Valuable Resources 解题报告
    BestCoder16 1002.Revenge of LIS II(hdu 5087) 解题报告
    codeforces 374A Inna and Pink Pony 解题报告
    codeforces 483B Friends and Presents 解题报告
    BestCoder15 1002.Instruction(hdu 5083) 解题报告
    codeforces 483C.Diverse Permutation 解题报告
    codeforces 483A. Counterexample 解题报告
    NSArray中地内存管理 理解
  • 原文地址:https://www.cnblogs.com/zhangbiao97/p/13062036.html
Copyright © 2020-2023  润新知