题目描述
实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须 原地 修改,只允许使用额外常数空间。
示例 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());
}
};