这是我参与11月更文挑战的第20天,活动详情查看:[2021最后一次更文挑战]
题目
实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列(即,组合出下一个更大的整数)。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须 原地 修改,只允许使用额外常数空间。
示例 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
思路
这题要求我们快速的根据例子,找到获取下一个排列的方案,然后使用代码将其实现。
这又是一道有关数字字典序的问题,不过与之前有一道题从左到右,使用单调栈写法寻找下一个更大或者更小的数不同的是,这次我们希望变化尽可能的小,因为我们遵循单调的思想,从右向左判断,直到不再单调,然后我们在之前排好序的部分找出大于目标的最小的一个数的下标,将两数交换。
关于获取大于一个数的最小数,我们使用二分法。
然后呢?我们需要将后续的数从小到大排序,我们应该怎么做?
当然使用排序不行的o(nlogn),或者说有更好的方式。
由于右边部分的数组其实本来是从大到小排好序的,只是现在来了一个数是为排序的,因此我们先将那个交换到右边的数找好位置,然后将整个右边部分翻转一下就行,这样子的时间复杂度是o(n)
代码
for(let i = nums.length - 1; i >= 0; i--){
for(let j = nums.length - 1; j > i; j--){
if(nums[j] > nums[i]){
[nums[j],nums[i]] = [nums[i],nums[j]]; // 交换
// 深拷贝[i + 1, nums.length)部分到新数组arr
let arr = nums.slice(i+1);
// arr升序排序
arr.sort((a,b) => a - b);
// arr替换nums的[i + 1, nums.length)部分
nums.splice(i+1,nums.length - i, ...arr);
return;
}
}
}
return nums.sort((a,b) => a - b); // 不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
};