LeetCode系列记录我学习算法的过程。
携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第30天,点击查看活动详情
题目
整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
- 例如,
arr = [1,2,3],以下这些都可以视作arr的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1]。
整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。
- 例如,
arr = [1,2,3]的下一个排列是[1,3,2]。 - 类似地,
arr = [2,3,1]的下一个排列是[3,1,2]。 - 而
arr = [3,2,1]的下一个排列是[1,2,3],因为[3,2,1]不存在一个字典序更大的排列。
给你一个整数数组 nums ,找出 nums 的下一个排列。
必须 原地 修改,只允许使用额外常数空间
示例:
输入: nums = [1,2,3]
输出: [1,3,2]
输入: nums = [3,2,1]
输出: [1,2,3]
输入: nums = [1,1,5]
输出: [1,5,1]
提示:
1 <= nums.length <= 1000 <= nums[i] <= 100
思路
这个题目的思路也是要先列出一下排列来找规律才能捋清楚思路,例如:
[2, 1, 3, 4, 5] -> [2, 1, 3, 5, 4]: 4和5进行交换
[2, 1, 3, 5, 4] -> [2, 1, 4, 3, 5]: 3和4先交换,然后对后两位进行升序排序
[2, 3, 5, 4, 1] -> [2, 4, 1, 3, 5]: 3和4先交换,然后对后三位进行升序排序
可以发现都是从后往前找到第一个不属于升序排列的数,记录其下标 tempIdx,然后和后面比他稍大一点的数进行交换,然后再把 tempIdx 后面数组项进行升序排列即可
有个特殊情况就是当整个数组呈降序排列时,没有下一个排列,所以返回整个数组反转后的值
代码实现
/**
* @param {number[]} nums
* @return {void} Do not return anything, modify nums in-place instead.
*/
var nextPermutation = function(nums) {
// 首先定义变量存储数组长度,以及一个获取从后往前第一个不符合升序排列的数组项的下标
let len = nums.length, tempIdx = nums.length - 1
// 当数组长度为 1 时,直接返回该数组
if(tempIdx === 0) return nums
// 从后往前找到第一个不符合升序排列的数组项
for(let i = tempIdx - 1; i >= 0; i--) {
if (nums[i] < nums[i + 1]) {
// 记录其下标,并跳出循环
tempIdx = i
break
}
}
// 如果找到的下标为最后一项,即整个数组呈降序排列
// 这时之间返回数组反转后的值即可
if (tempIdx === (len - 1)) {
return nums.reverse()
}
// 定义是否交换的标识,及存储 tempIdx 后面的所有数组项
let flag = false, tempArr = []
// 从后往前遍历到 tempIdx 项
for(let i = len - 1; i > tempIdx; i--) {
// 当还未交换且当前项大于需要交换项时
if(!flag && nums[i] > nums[tempIdx]) {
// 进行交换,并修改标识符
[nums[tempIdx], nums[i]] = [nums[i], nums[tempIdx]]
flag = true
}
// 将遍历项存入临时数组
// 因为是从后往前遍历的,所以直接是存储的升序排列
tempArr.push(nums[i])
}
// 将 tempIndx 后面的数组项替换为 临时数组里的数据
nums.splice(tempIdx + 1, len - tempIdx - 1, ...tempArr)
// 返回替换后的数组
return nums
};