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 1≤nums.length≤100
- 0 ≤ n u m s [ i ] ≤ 100 0 \le nums[i] \le 100 0≤nums[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%
*/