LeetCode 31 下一个排列

240 阅读2分钟

「这是我参与2022首次更文挑战的第12天,活动详情查看:2022首次更文挑战」。

题目:给定一个数组,将数组中元素顺序排列组成一个数字,将这些数字重新排列,得到下一个比当前数大的数字(要求所有数字全排列之后从小到大排序,当前数字的下一个数字),如果不存在比当前数字大的数字,则返回全排列最小的那个数字。要求,在原数组上进行更改。

解题思路

一开始的简单思路,得到所有元素的全排列,将这些数字从小到大排序,遍历序列,找到与当前元素相同的元素,返回其下一个序列,转为数组。这样思路可行,但题目要求在原数组上进行更改,很明显这种思路是不符合的。参考题解之后:

要得到所要求的数字,那么必须将数组后面的大数字和前面的小数字进行交换,并且交换之后还得保证原小数字之和的元素保持有序。

步骤如下:

  1. 首先数组元素从后往前遍历,找到符合a[i-1]<a[i]的这个序列,保存索引i-1,其为上述前面的小数字。
  2. 判断索引i-1,如果为0且满足a[0]>a[1],则代表当前数组严格降序,将数组重排结束即可。否则i-length-1必然是严格降序,此时将数组从后往前遍历,找到第一个元素符合a[j]>a[i-1]的,交换彼此。
  3. 将数组i-length-1的元素进行排序,nums即为最终结果。

代码如下:

public static void nextPermutation(int[] nums) {
        int len = nums.length;
        if(len<=1) return;
        int index = len-1;
        for(;index>0;index--){
            if(nums[index-1]<nums[index]){
                index--;
                break;
            }
        }
        if(index==0&&nums[0]>nums[1]){
            Arrays.sort(nums);
            return;
        }
        for(int i=len-1;i>index;i--){
            if(nums[i]>nums[index]){
                swap(nums, index, i);
                break;
            }
        }
        reverse(nums, index+1, len-1);
    }

    public static void swap(int[] nums, int a, int b){
        int temp = nums[a];
        nums[a] = nums[b];
        nums[b] = temp;
    }

    public static void reverse(int[] nums, int start, int end){
        while(start<end){
            swap(nums, start++, end--);
        }
    }

时间复杂度为O(N)O(N),空间复杂度为O(1)O(1)。看了一下官方的,思路一致更加简练,但上述代码逻辑清晰,比较友好。