[每日一题]111:下一个排列


题目描述

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

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

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

示例 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]

题解思路

方法一:两遍扫描

注意到下一个排列总是比当前排列要大,除非该排列已经是最大的排列。我们希望找到一种方法,能够找到一个大于当前序列的新序列,且变大的幅度尽可能小。具体地:

  • 我们需要将一个左边的「较小数」与一个右边的「较大数」交换,以能够让当前排列变大,从而得到下一个排列。
  • 同时我们要让这个「较小数」尽量靠右,而「较大数」尽可能小。当交换完成后,「较大数」右边的数需要按照升序重新排列。这样可以在保证新排列大于原来排列的情况下,使变大的幅度尽可能小。

复杂度分析

  • 时间复杂度:O(N),其中 N 为给定序列的长度。我们至多只需要扫描两次序列,以及进行一次反转操作。
  • 空间复杂度:O(1),只需要常数的空间存放若干变量。

代码实现:

class Solution {
    
    
public:
    void nextPermutation(vector<int>& nums) {
    
    
        int i = nums.size() - 1;
        while (i > 0 && nums[i - 1] >= nums[i]) {
    
    
            --i;
        }
        if (i > 0) {
    
    
            int j = nums.size() - 1;
            while (j > 0 && nums[i - 1] >= nums[j]) {
    
    
                --j;
            }
            swap(nums[i - 1], nums[j]);
        }
        reverse(nums.begin() + i, nums.end());
    }
};

方法二:使用 STL 库(没有意义)

next_permutation函数:

  组合数学中经常用到排列,这里介绍一个计算序列全排列的函数:next_permutation(start,end),和prev_permutation(start,end)。这两个函数作用是一样的,区别就在于前者求的是当前排列的下一个排列,后一个求的是当前排列的上一个排列。至于这里的“前一个”和“后一个”,我们可以把它理解为序列的字典序的前后,严格来讲,就是对于当前序列pn,他的下一个序列pn+1满足:不存在另外的序列pm,使pn<pm<pn+1

对于 next_permutation 函数,其函数原型为:

#include <algorithm>
bool next_permutation(iterator start,iterator end)

当当前序列不存在下一个排列时,函数返回false,否则返回true

  • 如果当前是最大序列 next_permutation 函数的下一个则是 最小序列 (已验证

代码实现:

class Solution {
    
    
public:
    void nextPermutation(vector<int>& nums) {
    
    
        next_permutation(nums.begin(), nums.end());
    }
};

猜你喜欢

转载自blog.csdn.net/AngelDg/article/details/114370206