力扣31-下一个排列| 8月更文挑战

204 阅读2分钟

原题链接

leetcode-cn.com/problems/ne…

题目释意

原题目的意思可能不是很容易搞懂,实际上题目描述的意思如下:

给定一个数组(假定为[1,2,3]),这些数按给定的顺序组成一个数(123),那么求问:

是否存在一个由数组中的这些数组成的,且在由这些数组成的数的升序集合中([123,132,213,231,312,321]),为给定顺序的下一个的数?(按照给定的例子为132

  • 如果有,则返回结果按顺序拆解的数组([1,3,2]),如果没有(说明组合成的数最大),那么按升序排序并返回。

解析

我们试着来分解上面的问题:

1.按照给定的顺序是否是最大的数?

2.如果是,如何取所要的序列下一值?

3.如果不是,排序

例子:

6 5 1 3 4 2

排序我们交给API来高效完成:

Arrays.sort()

我们先来解决问题1。

容易知道:

  • 常识1:越大的数字,在后面是越小的,例如:654>564>546

我们将数放在某个位置上,最终表示出来的结果为这个数放在这个位置上所具有的价值,根据常识1我们可以得知:

推论1:越大的数字在越前面,给最终结果带来的价值是越大的

那么我们可以这样子判断一个数是否是这些数组合出来最大的

  • 流程1:我们从数组的后面往前取数,根据我们的常识,按照从后往前的顺序依次读数,如果从后面读出来的数不小于前面的(例如:654321,1<2<3<4<5<6,按后面往前面读的顺序(123456),读到的数依次增大),就意味着那么这个数就是这个序列里最大的。

现在我们知道了如何判断一个数是否是最大的了,接下来我们需要解决问题2。

问题2的前置流程,就是在流程1中我们取到了一个小于之前读出来的数的最大值的情况。

  • 根据我们的推论1,我们可以推出推论2:

    • 推论2:如果前面的数(索引为i)小于后面的数,那么让这一位的数变大且是变大的情况里最小的情况为:

      从大于这个数的地方往后找,找到大于这个数且最小的数,两个数互相调换。

      例如:

      6 5 1 3 4 2

      按照流程1我们读到1,发现1<3,我们从1后面的3 4 2 里面取出2,和1进行替换得到:

      6 5 2 3 4 1

如果我们还要使得后面的序列最小,我们只需要在替换后把后面的数排个序就好了。

我们将流程1和推论2具化成代码,就得到了最终的答案:

代码

public static void nextPermutation(int[] nums) {
    int lastMax = nums[nums.length-1];
    for (int i = nums.length - 2; i >= 0; i--) {
        if(lastMax<=nums[i]){
            lastMax = nums[i];
            continue;
        }
        int chgIdx = i+1,minButBigVal=lastMax;
        for (int i1 = i + 1; i1 <= nums.length - 1; i1++) {
            if(nums[i1]>nums[i] && nums[i1]<minButBigVal){
                chgIdx = i1;
                minButBigVal = nums[i1];
            }
        }
        minButBigVal = nums[i];
        nums[i] = nums[chgIdx];
        nums[chgIdx] = minButBigVal;
        Arrays.sort(nums,i+1,nums.length);
        return;
    }
    Arrays.sort(nums);
}

执行用时:1 ms, 在所有 Java 提交中击败了84.54%的用户

内存消耗:38.4 MB, 在所有 Java 提交中击败了79.83%的用户