题目详情
实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须 原地 修改,只允许使用额外常数空间。
示例 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,2,3],实际上就是123,我们找刚好比它大的数,就是132。
而115,也是十分明显,答案是151.
很明显,我们这里都涉及到了交换。
依然很明显,将整个序列中靠左的且小的数X和靠右的且大的数Y进行交换,整个序列就会变大。
当X的位置越右,整个序列变大的程度就会越小(这是我们所期望的)
当Y的值越小,整个序列变大的程度就会越小(这亦是我们所期望的)
所以我们希望找到一对X,Y,它们满足X在左,Y在右,且前者小,后者大。同时X位置尽量靠左,Y大小尽量小。
找X与Y的逻辑还是很好搞的。我们从后往前遍历,每次记录下当前遍历集合的最大值,然后将当前值与最大值进行比较,若当前值<最大值,则找到了X,否则继续往前遍历(当然很有可能找不到,此时序列肯定是形如4321的降序排列,我们按照题意将其reverse一下即可)。
那么Y如何找呢,Y是最大值吗?不一定。eg:1232654。
我们来走一下流程:
到5,max为4,不行
到6,max为5,不行
到2,max为6,此时行了。
我将2与6交换,则会得到1236 542。这是不对的。
但是我可以轻松举出反例:1234 652。
问题在于,Y不够小。
所以这里我们需要再次从后往前遍历,找到第一个大于X的值,这个值就是我们要找的真正的Y(这里不加证明的认为Y<=max,这个可以证明,但也很好想,不说了)。
一开始,本题我也是这样做的,但提交之后发现错误。
分析了一会儿发现了错误在哪。该题要求是刚好最大的序列。
就上面的列子,我们可以找到一个更小的答案:1234 256。这里我打了一个空格来区分。明显,这里进行了一个排序。这个确实就是最小的答案了,也就是正确的答案了。
实现也很简单,将X后面的值进行排序就ok。
至于为什么要这么做。
我们之前的交换能够导致整个序列变大,变大一个X->Y的10^i级别的变化(认为i是X从数组尾开始的索引)。但是很明显X之后的数值都是降序的排列,大的数字在高位,小的数字在低位。那么此时,将它们重新升序排序,就会比之前小。
所以交换是让序列变大一个数量级别,排序是让整个序列在该数量级别中达到自己所能达到的最小值。
代码实现
代码实现很简单,其中注释就是我思考的过程。
class Solution {
public void nextPermutation(int[] nums) {
//排列成字典序中下一个更大的排列
//首先了解下字典序把
//如果我理解的没错的话,应该就是交换顺序,使得新的数刚好大一点
//第一个例子是将2,3交换,因为
//从第一个元素开始往后,一直找,只要不是降序排列的一定存在某两个元素A,B。A<B
//那么将这个A与最后一个大值交换即可
//这个问题也就从后往前,找第一个比后面小的数,然后和最靠后比它大的数进行交换
//唯一找不到的情况就是,前面只有比后面大的,没有比后面小的
//从后面开始往前遍历,每次记录下所扫描过的最大值,然后将当前值与最大值进行比较
//如果小于,则找到,则将二者交换顺序,直接返回
//如果遍历完都没找到,那么将原数组reverse即可
//这题考虑的时候少考虑了,它是有重排序的
//它会把交换的之间进行后移
//比如132443
//不是后移,而是进行一个整体的重新排序
if(nums.length==0){
return;
}
int max=nums[nums.length-1];
int i;
for(i=nums.length-2; i>=0; i--){
if(nums[i]<max){
break;
}
else{
max = nums[i];
}
}
if(i!=-1){
//不是和max交换,而是找到一个大于nums[i]的最后一个数(逆序第一个)
int j = nums.length-1;
while(nums[j]<=nums[i]){
j--;
}
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
//将i+1之后的进行重排序
//因为不能用O(n)的空间,所以只能牺牲时间复杂度来进行排序了
//重新排序
Arrays.sort(nums,i+1,nums.length);
}
else{
//因为不能用O(n)的空间,所以只能牺牲时间复杂度来进行排序了
Arrays.sort(nums);
}
return;
}
}
动图
(出自该题LeetCode官方题解)
代码通过之后看题解,发现题解跟我是一样的想法,哈哈哈好开心,这里搬运一下,也便于我之后回来复习。