31. 下一个排列

31. 下一个排列

题目描述

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

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

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

示例1:

输入:nums = [1,2,3]
输出:[1,3,2]

示例2:

输入:nums = [3,2,1]
输出:[1,2,3]

示例3:

输入:nums = [1,1,5]
输出:[1,5,1]

示例4:

输入:nums = [1]
输出:[1]

提示:

  • 1 ≤ n u m s . l e n g t h ≤ 100 1 \le nums.length \le 100 1nums.length100
  • 0 ≤ n u m s [ i ] ≤ 100 0 \le nums[i] \le 100 0nums[i]100

题解:

下一个排列,通俗点来说就是:字典序 大于当前排列且最小 的排列。

一个直观想法是:用后面的较大数字替换前面较小的数字,肯定能构造一个字典序更大的排列,但是怎么构造 大且最小 的排列呢?

有这样一个规律,每一个排列从末尾元素开始往前遍历,肯定有一段递增序列,比如:1 3 4 2。极限情况从尾到头递增,比如:4 3 2 1。

这里有一个贪心思路:

对于这个从后往前的递增序列,如果我们将破坏这个递增特性的第一个元素:a[i] < a[i+1] 和后面第一个大于 a[i] 的元素进行交换,那么满足新的排列大于当前排列,并且字典序尽可能的小,如 1 3 4 2 -> 1 4 3 2 ,但是 1 4 3 2 并不是字典序最小的排列,因为无论交换前还是交换后,后面一段递增的特性是不变的,想要字典序更小,可以把这部分逆序,变成递减,即 1 4 3 2 -> 1 4 2 3 ,这才是最终的结果。

举个例子,假设 nums = [ 1, 2, 3, 4]

第一个排列:[1, 2, 3, 4]

第二个排列:[1, 2, 4, 3]

第三个排列:[1, 3, 2, 4]

第四个排列:[1, 3, 4, 2]

第五个排列:[1, 4, 2, 3]

我们可以发现,在第四个排列到第五个排列中,是将 nums[2]nums[1] 交换,然后将 nums[2...3] 部分逆序。

时间复杂度: O ( n ) O(n) O(n)

额外空间复杂度: O ( 1 ) O(1) O(1)

代码:

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        int n = nums.size();
        if ( n < 2 ) return;
        int i, j;
        for ( i = n - 2; i >= 0; --i ) {
            if ( nums[i] < nums[i + 1] ) break;
        }
        if ( i >= 0) {
            for ( j = n - 1; j > i; --j ) 
                if ( nums[j] > nums[i] ) break;
            swap( nums[i], nums[j] );
        }
        reverse( nums.begin() + i + 1, nums.end() );
    }
};
/*
时间:0ms,击败:100.00%
内存:11.7MB,击败:99.68%
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值