2021.8.2 力扣-下一个排列

题目描述:

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

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

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

方法一:

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        int n = nums.size();
        int i;
        for (i = n - 2; i >= 0; i--)
        {
            int j = i + 1;
            int minx = INT_MAX;
            int min_maxnum  = INT_MIN;    //记录所有大于nums[i]的值中的最小值
            int index = -1;               //min_maxnum的下标
            while (j < n)
            {
                if (nums[j] > nums[i] && nums[j] - nums[i] < minx)
                //寻找排在nums[i]的后面的数字中,大于nums[i]且两者差距最小的值
                {
                    minx = nums[j] - nums[i];
                    min_maxnum = nums[j];
                    index = j;
                }
                j++;
            }
            if (min_maxnum > nums[i])
            {
                swap(nums[i], nums[index]);     //交换
                break;
            }
        }
        sort(nums.begin() + 1 + i, nums.end()); //将后续的数字从小到大排列
    }
};

时间复杂度为O(n²),空间复杂度为O(1)。

文字不太好表达,举个例子用于该方法的应用:

样例{1,2,5,3,6,4}:首先i=5,nums[i]=6,nums[i]的右边没有比6还大的数字, 结束;然后i=4,nums[i]=3,nums[i]的右边比3还大的数字有6和4,其中和3差距最小的为4,则min_maxnum=4,index=5,交换3和4,数组变为{1,2,4,6,3},退出循环。最后将4后面的数字排序,数组变为{1,2,4,3,6},即为所求。

另外,在我的vs编译器中,当i=-1时,nums.begin()+i+1会报错,理由是cannot seek vector iterator before begin,只有改成nums.begin()+1+i才能过,而在leetcode的编译器里倒是都可以,反而前者还能够更快达到0ms。

方法二:

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        int n = nums.size();
        int i = n-2;
        while (i >= 0 && nums[i] >= nums[i + 1])    
        //从右起,找到第一个小于右边元素的数
        {
            i--;
        }
        if (i < 0) 
        //i=-1,说明这个数组是降序序列,不可能还有更大的排列方式了,直接翻转取最小排列
        {
            reverse(nums.begin(), nums.end());
            return;
        }
        for (int j = n - 1; j > i; j--)
        //从右起,寻找第一个比刚才找到的nums[i]大的数将两者交换
        {
            if (nums[j] > nums[i])
            {
                swap(nums[i], nums[j]);
                break;
            }
        }
        //显然,交换完之后,新的nums[i]的右边为降序序列,将右边翻转为升序序列
        reverse(nums.begin() + i + 1, nums.end());
    }
};

来自官方的题解,这个方法解题思路和我的方法一是差不多的,都是找到一个位于右边的较大值和一个位于左边的较小值,将两者交换,并把右边的序列变为升序序列。但是官方的这个方法更为巧妙,分别进行了两次扫描,而且没有用到sort()函数,时间复杂度只为O(n),空间复杂度也为O(1)。

注意,reverse()函数的时间复杂度为O(n)。

另外该方法也在在 C++ 的标准库函数 next_permutation 中被采用,在评论中看到一个神奇的解法

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值