Leetcode 31.下一个排列

276 阅读2分钟

要求:

实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。

如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。

必须原地修改,只允许使用额外常数空间。

示例:

左边输入,右边输出:
1,2,31,3,2
3,2,11,2,3
1,1,51,5,1

思路:

  • 改动数字应该是低位的较大的数,换到高位去,换完之后,更换的那个高位往后直至低位重新再排序,按从大到小排序即可
  • 如12345:5是最低位,右边没有数字可以进行交换。然后看4,右边的5比4大,所以4,5互换即可,得到12354
  • 如123654:从4开始往前找,4的右边没有数字可以与其交换。然后5,右边没有比它大的数字,然后6,右边没有更大的数字,然后3,可以与4互换,得到下一个更大的序列。
  • 从低位开始,若nums[i]>=nums[i+1]说明nums[i]更大,不做处理。不断递推,直至i=0,说明当前的排列是最大的,只能全部逆序输出最小的了。
  • nums[i]<nums[i+1]说明,nums[i]右边的数字有比它更大的,此时就需要一个循环去找到nums[i]右边中比它大的数字里面最小的那一个。而nums[i]右边的元素是递减排列的,所以依旧是从右往左找,找到第一个大于nums[i]的元素。for(;j>i && nums[j] <= nums[i]; j--),j就是用于交换的元素的索引
  • 交换完之后,需要将i右边的元素全部重新排列,按升序排列,即[i+1, nums.length-1]的排序。
  • 升序排序中,交换过后的数组是降序排序的,所以用双指针原地交换即可。

代码:

class Solution {
    public void nextPermutation(int[] nums){
        int n = nums.length - 1;
        for (int i = n-1; i > 0; i--) {
            if (nums[i] > nums[i+1]) {
                int j = n;
                for(; i<j && nums[i]>nums[j]; j--);
            exchange(nums, i, j);
            reverse(nums, i+1, n);
            return;//此处直接返回,是对于存在nums[i]>nums[i+1]的情况来说的
            }
        }
        reverse(nums, 0, n);//当前序列是最大序列,直接逆序处理
    }
    public void exchange(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
    public void reverse(int[] nums, int i, int j) {
        while (i < j) {
            exchange(nums, i++, j--);
        }
    }
}