【leetcode】31. Next Permutation
题目说明
https://leetcode-cn.com/problems/next-permutation/description/
实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须原地修改,只允许使用额外常数空间。
解法1
找到下一个更大序列,可以理解为将数字序列想像成由这些数字序列组成的一个数字,要找到相同数字组合的下一个更大数字。
定点
从序列的右边开始遍历,找到第一个相邻的数字序列[i,i+1]满足i+1的值大于i的值,那么从i到数字序列结尾就需要重新排列了。
比如说[1,2,3,4,5,6,2,1],第一个满足上述条件的是5与6,需要将5到结尾的序列重新排列。
这里需要注意的是如果满足上述条件,[i+1,nums.size() -1]区间必定是一个降序序列。
交换
首先要找到最近的较大值,我们需要在[i+1,nums.size() -1]的区间找到最小的比nums[i]大的值,然后与nums[i]进行交换。
由于该区间是降序序列,从后往前遍历第一个比nums[i]大的位置即是我们要找的。
上述例子[1,2,3,4,5,6,2,1],6是最小的比5大的值,需要与5交换:[1,2,3,4,6,5,2,1]
反转
交换以后,得到的序列并不是“下一个”更大的序列,还需要将[i+1,nums.size() -1]进行反转,将较小的数移至高位,才能得到满足条件的序列。
上述例子,将[1,2,3,4,6,5,1]中的[5,2,1]进行反转,得到序列[1,2,3,4,6,1,2,5],即最终结果。
特殊地,若找不到第一个相邻的数字序列[i,i+1]满足i+1的值大于i的值,即本身序列是个降序序列,则需要将整个序列整体反转。
代码如下:
/*
* 时间复杂度:O(n)
*/
void nextPermutation(vector<int>& nums) {
int tmp = 0;
int i = 0;
//找到第一个相邻的数字序列[i,i+1]满足i+1的值大于i的值
for(i = nums.size() - 2; i >= 0; i --){
if (nums[i] < nums[i + 1]){
break;
}
}
if (i >= 0){
int j = 0;
//从后往前遍历第一个比nums[i]大的位置
for (j = nums.size() - 1; j >= 0; j --){
if (nums[j] > nums[i]){
break;
}
}
swap(nums, i, j);
}
//反转序列
revert(nums, i + 1);
return;
}
int swap(vector<int>& nums, int i, int j)
{
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
return 0;
}
int revert(vector<int> & nums, int start)
{
int i = start;
int j = nums.size() - 1;
while(i < j){
swap(nums,i,j);
i ++;
j --;
}
return 0;
}