这是我参与8月更文挑战的第20天,活动详情查看:8月更文挑战
又到了周五,每次到了周五都会感叹一下时光飞逝,1周过去,每天都很忙,但是1周过去好像成果也不明显。
昨天和今天连续2天都没锻炼,立下的flag快到倒了,接下来这个周末的时间更要抓紧了,还有好多计划的事情还没完成,比如想要看的书,关于沟通、关于自动驾驶,还有下周想做的1个关于可转债的分享,还有很多很多类似的事情。周末的时间又特别短暂,希望本周能把这些事情都完成吧。
不过还好,每日做leetcode和更文的习惯还没断,特别是昨天回家都11点多了,最后还是赶在12点之前完成了。今天继续,做leetcode的第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个比右边数字小的数,下标为high
- 从右往左,找到第1个比num[high]大的数,下标为low
- 交换num[high]和num[low]
- 反转num[high+1]~num[len-1]
当然,特殊情况要先处理掉,如果第1步找不到high,证明真个数组单调递减,已经是这些数组能表示的最大数值的的,要轮回到最小,直接反转即可。
为什么可以这样做呢?
我们可以这样想,我们要找到一个比当前数字值更大的数,必须是跟当前数字比,更高位有更大的数。在满足更大的前提后,这个数值还要尽量的小,所以这个数值最大的更高位就要尽量靠右。我们可以假设存在下标high,high右边是一个单调递减的序列,那么high右边的数字无论怎么交换,都只会更小,不会更大,所以,必须交换high。那用哪一位交换呢,肯定是要比num[high]更大,同时,要尽量靠右,为什么?因为high右边是1个单调递减的序列,越靠右的数值越小。这回我们明白了如果找到跟high交换的low,但是第4步又是为什么呢?这是因为,high这1位交换成1个更大的数值后,无论后面这么排列,都会比原来的数值更大了,为了获取最小值,我们应该把high右边的数值从小到大排列,我们直到,交换之前,high右边是个单调递减的序列,num[low]又是右边第1个比num[high]大的数值,所以交换后,high的右边还是一个单调递减的序列,想要实现从小到大排列,刚好只要反转即可。
Java版本代码
class Solution {
public void nextPermutation(int[] nums) {
int len = nums.length;
// 定义高位和低位的下标
int high = -1, low = -1;
for (int i = len - 1; i > 0; i--) {
if (nums[i-1] < nums[i]) {
high = i - 1;
break;
}
}
if (high == -1) {
// 证明未找到比后一个数字小的数字下标,整个数组为降序,下一个排列为全部反转成最初始的升序
int left = 0, right = len - 1;
while (left < right) {
swap(nums, left, right);
left++;
right--;
}
return;
}
// 找到了high,去找能跟它交换的low,这个low要尽量靠右
for (int i = len - 1; i > high; i--) {
if (nums[i] > nums[high]) {
low = i;
break;
}
}
// 交换high和low,交换后反转high后面的数组
swap(nums, high, low);
int left = high + 1, right = len - 1;
while (left < right) {
swap(nums, left, right);
left++;
right--;
}
return;
}
/**
* 交换数组中的2个值
* @param nums
* @param a
* @param b
*/
private static void swap(int[] nums, int a, int b) {
nums[a] += nums[b];
nums[b] = nums[a] - nums[b];
nums[a] -= nums[b];
}
}