leetcode刷题(74)——31. 下一个排列

一、题目

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

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

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

以下是一些例子,输入位于左侧列,其相应输出位于右侧列。

1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1

二、思路

思路分析:

我们先理解一下 “下一个排列” 的定义:给定数字序列的字典序中下一个更大的排列。如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。

可以这样理解 字典序:把数字序列的一个排列看成一个整数,所有排列从小到大的顺序就是字典序。比如,序列 1,2,3,4,5,6,其排列依次为:

123456
123465
123546
...
654321

且有 123456 < 123465 < 123546 < … … <654321,这就是字典序。

这样我们就可以把原问题转化为:将给定的排列组合为一个整数,我们的目标就是对这些数字重新排列,找到下一个更大的整数。如 123 下一个更大的数为 132。如果没有更大的整数,则输出最小的整数。

怎么找下一个排列:

  • 首先,怎么找更大的排列: 举个例子,要找到比 123456 更大的排列,只需要将后面一个较大的数(这里我们称它为 “大数”)和前面一个较小的数(这里我们称为 “小数”)交换即可,比如 6 和 5 交换得到 123465,6 和 4 交换得到 123654;
  • 而我们要找的是下一个排列,也就是希望找到的下一个数增加的幅度尽可能的小,如上例我们要找的是 123465 而不是 123654。我们可以这样实现:
    1、在尽可能靠右的低位进行交换,这样数字的增幅相对较小,这就需要从后往前查找;
    2、将一个尽可能小的 “大数” 和它前面的 “小数” 交换。比如 123465,应该把 5 和 4 交换,而不是把 6 和 4 交换;
    3、完成交换之后,需要将 “大数” 之后的所有数字置为升序,升序排列就是最小的排列。比如,123465 交换 5 和 4 之后得到 123564,将 “大数” 5之后的数字置为升序,得到 123546。显然,123546 < 123564,所以 123546 才是 123465 的下一个排列。

算法实现:

  • 从后往前查找第一对相邻升序的元素,索引分别为 i 、 j i、j ij ( i < j i < j i<j),且满足且 A [ i ] < A [ j ] A[i] < A[j] A[i]<A[j],如 123465 中的 46。此时, [ j , e n d ) [j,end) [j,end) 必然是降序的。 A [ i ] A[i] A[i] 就是上面分析中的 “小数”
  • [ j , e n d ) [j,end) [j,end)从后往前查找第一个大于 A [ i ] A[i] A[i] 的数 A [ k ] A[k] A[k] A [ k ] A[k] A[k] 就是上面分析中的 “大数”
  • 交换 A [ i ] A[i] A[i] A [ k ] A[k] A[k]
  • 这时, [ j , e n d ) [j,end) [j,end) 仍然是降序的,将 [ j , e n d ) [j,end) [j,end) 置为升序;
  • 如果在第一步找不到符合条件的相邻元素对,说明当前 [i,end) 为一个降序顺序,也就是不存在下一个更大的排列,则直接跳到步骤 4。

三、代码

class Solution {
    public void nextPermutation(int[] nums) {
        if(nums == null || nums.length < 2)
            return;
        int i = 0;
        int j = nums.length - 1;
        boolean flag = false;// 设置标志位,如果有下一个更大的排列将该位置为true
        // 从左往右查找第一对相邻升序的元素
        while(j != 0){
            if(nums[j - 1] < nums[j]){
                i = j - 1;
                flag = true;
                break;
            }else{
                j--;
            }
        }
        if(flag){// 有下一个更大的排列
            int k = nums.length - 1;
            while(k != 0){
                if(nums[i] < nums[k]){
                    swap(nums, i, k);
                    shengXu(nums, j, nums.length - 1);
                    break;
                }else{
                    k--;
                }
            }
        }else{// 没有下一个更大的排列,将序列置为升序
            shengXu(nums, j, nums.length - 1);
        }
    }

    public void swap(int[] nums, int i, int j){
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

    // 把降序数组置为升序
    public void shengXu(int[] nums, int start, int end){
        while(start < end){
            swap(nums, start, end);
            start++;
            end--;
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值