力扣LeetCode #31 下一个排列(NextPermutation)

该博客详细解析了LeetCode中的#31题目,探讨如何找到一个数字序列的下一个字典序排列。内容包括题目描述、示例、解题思路和JAVA代码实现。思路分析部分指出,需找到数组中第一个逆序对并交换,再将后续部分排序以确保最小的增加。
摘要由CSDN通过智能技术生成

- 题目描述

实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。必须原地修改,只允许使用额外常数空间。

来源:LeetCode

- 示例

以下是一些例子,输入位于左侧列,其相应输出位于右侧列。
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1
1,2,4,3 → 1,3,2,4
1,2,5,4,6,3 → 1,2,5,6,3,4

- 思路分析

刚开始看了测试用例,只考虑到了改变两位的情况,输入之后发现不对,就自己又想了一些简单的测试用例,才发现了真正的规律。
题目有两个要求:

  1. 如果不存在更大的排列,则输出最小的排列。
    这个很容易实现,从数组头开始,看是否存在 n u m s [ i ] < n u m s [ i + 1 ] nums[i]<nums[i+1] nums[i]<nums[i+1]即可。如果存在,说明一定有更大的排列;如果不存在,则首尾交换数组元素即可。
  2. 如果存在更大的排列,则输出下一个更大的排列。
    因为希望找到的下一个大的排列尽可能小,因此考虑改变尽可能少的尾部数字。观察可以发现,如果 n u m s [ i ] > = n u m s [ i + 1 ] nums[i]>=nums[i+1] nums[i]>=nums[i+1],则这两位数已经是最大,因此需要找到的是从尾部向前的第一个 n u m s [ i ] < n u m s [ i + 1 ] nums[i]<nums[i+1] nums[i]<nums[i+1]。这个 i i i就是我们开始改变的地方。找到 i i i后,我们现在已知的关系是 n u m s [ i ] < n u m s [ i + 1 ] > = n u m s [ i + 2 ] > = . . . > = n u m s [ l e n − 1 ] nums[i]<nums[i+1]>=nums[i+2]>=...>=nums[len-1] nums[i]<nums[i+1]>=nums[i+2]>=...>=nums[len1],为了使得到的数尽可能小,我们从 n u m s [ i + 1 ] , n u m s [ i + 2 ] , . . . , n u m s [ l e n − 1 ] nums[i+1], nums[i+2], ... , nums[len-1] nums[i+1],nums[i+2],...,nums[len1]中选出比 n u m s [ i ] nums[i] nums[i]大的最小元素与 n u m s [ i ] nums[i] nums[i]交换,然后将 n u m s [ i + 1 ] , n u m s [ i + 2 ] , . . . , n u m s [ l e n − 1 ] nums[i+1], nums[i+2], ... , nums[len-1] nums[i+1],nums[i+2],...,nums[len1]按照从小到大排列即可。

- JAVA实现

public static void nextPermutation(int[] nums) {
    if(nums.length == 0) return;
    int s, len = nums.length, flag=0;    //flag用来判断是否已经是最大
    for(int i=0;i<len-1;i++) {
        if(nums[i]<nums[i+1]) {
        	flag = 1;
            break;
        }
    }
    
    //flag=0, 则已经是最大的排列
    if(flag == 0) {
        decreasingToIncreasingSort(nums, 0, len-1);
    }
    else {
        //从尾部开始找第一个nums[i]<nums[i+1]
        for(s=len-2;s>=0;s--) {
            if(nums[s]<nums[s+1]) break;
        }
        if(s+1 == len-1) swap(nums, s,s+1); //如果只有后两位数需要改变
        else {
            if(nums[s]<nums[len-1]) {  //如果最后一位数也大于nums[s]
                swap(nums,s,len-1);
                decreasingToIncreasingSort(nums,s+1,len-1);
            }
            else { //找到大于nums[s]的最小的数
                for(int j=s+1;j<len-1;j++) {
                    if(nums[s]>=nums[j+1] && nums[s]<nums[j]) {
                        swap(nums,s,j); //交换之后,从nums[s+1]到nums[len-1]仍然符合降序排列
                        decreasingToIncreasingSort(nums, s+1, len-1);
                    }
                }
            }
        }
    }
}

//交换数组中两个数的位置
public static void swap(int[] nums, int i, int j) {
    int mid = nums[i];
    nums[i] = nums[j];
    nums[j] = mid;
}

//头尾交换,使最大排列变成最小排列
public static void decreasingToIncreasingSort(int[] nums, int start, int end) {
    while(start<end) {
        swap(nums, start, end);
        start++;
        end--;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值