题目描述
实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列(即,组合出下一个更大的整数)。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须 原地 修改,只允许使用额外常数空间。
样例描述
示例 1:
输入:nums = [1,2,3]
输出:[1,3,2]
示例 2:
输入:nums = [3,2,1]
输出:[1,2,3]
思路
- 两次遍历。遍历方向是从后往前,由题目,需要找下一个排列比当前排列大,但是要尽可能使大的幅度最小,也就是高位尽可能不变,低位稍微变化。因此选择从后往前遍历。
- 具体做法:从后往前寻找第一个满足a[i] < a[i + 1] 的顺序对,如果不存在,那说明整个排列是降序的,下一个排列就是最小的升序。 如果存在,记录这个数,从这个位置往后找第一个比这个数大的数,然后交换。
- 显然交换后,这个位置后面的数仍然是降序的,很好证明。 要使得增加的幅度最小,将后面这部分降序序列逆转成升序即可。
如下具体过程图:
从后往前找第一个顺序对
从后往前找第一个大于5的数,然后交换
后半部分逆序,使得下一个序列的增幅尽可能小
- 小细节:一定要找严格小于的左边较小数,以及右边的必须严格大于左边的较小数。
代码
class Solution {
public void nextPermutation(int[] nums) {
int i = nums.length - 2; //-2是要防止越界,因为后面要表示i + 1
//从后往前,找第一对顺序对,确定左边的较小数(必须严格小于右边)
//防止越界
while (i >= 0 && nums[i] >= nums[i + 1]) {
i --;
}
//没有越界。 如果越界了说明 整个都是降序排列 已经是最大了
if (i >= 0) {
//从后往前寻找第一个比左边较小数大的数 进行交换
int j = nums.length - 1;
while (j > i && nums[i] >= nums[j]) {
j --;
}
swap(nums, i, j);
}
//两种情况都能直接用这个逆转, 因为都是后面部分
reverse(nums, i + 1, nums.length - 1);
}
//交换函数
public void swap(int nums[], int x, int y) {
int t = nums[x];
nums[x] = nums[y];
nums[y] = t;
}
//逆置函数
public void reverse(int nums[], int left, int right) {
while (left < right) {
swap(nums, left, right);
left ++;
right -- ;
}
}
}