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 <= nums.length <= 100
  • 0 <= nums[i] <= 100

思路

最重要的是弄清楚什么是下一个排列?这个排列“递增”的规律是什么?

排列组合的顺序是自小到达逐渐演变的;例如:对于5个数字的排列12354和12345,排列12345在前,排列12354在后。按照这样的规定,5个数字的所有的排列中最前面的是12345,最后面的是54321。

全排列的算法如下:

设A是1~n的一个全排列:A=a1a2…an。

1)从排列的最右端开始,找出第一个比右边数字小的数字(即 ​​a[i] < a[i+1]​​​)的序号i(i为数组下标);
2)在a[i]的右边的所有数字中(即​​​[i + 1, n]​​​中),从右到左找出​​第一个比a[i]大的数​​​的序号j;(因为右边的数从右至左是递增的) ;
3)对换a[i]和a[j]
4)再将a[j + 1] … a[n]反转,这就是​​​下一个排列​​。

这题更像是一个数学题,知道排列的规律很好处理,但是…

代码

class Solution {
public void nextPermutation(int[] nums) {
int n = nums.length;
int left = n - 2;
// 从后往前找到第一个nums[i] > nums[i-1]的值,将(i-1)赋值给left
while (left >= 0 && nums[left] >= nums[left + 1]) {
left --;
}
// 可能当前排列是最大的,所以要判断left >= 0
if (left >= 0) {
int j = nums.length - 1;
// 从[left + 1, n]中找到靠右侧第一个大于nums[left]的值
while (j >= 0 && nums[left] >= nums[j]) {
j--;
}
swap(nums, left, j);
}
//将[left + 1, n]位置的数据正序排序(反转后的即为正序排序)
reverse(nums, left + 1, n - 1);
}

private void swap(int[] nums, int left, int right) {
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
}

private void reverse(int[] nums, int left, int right) {
while (left < right) {
swap(nums, left, right);
left++;
right--;
}
}
}

复杂度分析

  • 时间复杂度:O(N),其中N 为数组长度。我们至多只需要扫描两次序列、进行一次反转操作。
  • 空间复杂度:O(1),存放诸如n、left等变量