题目要求
给定一个整数数组 nums,找到它的下一个排列,即字典序中下一个比它更大的排列。如果不存在下一个排列,则将 nums 重新排列为字典序最小的排列(即升序排列)。
输入输出格式:
- 输入:一个整数数组
nums。 - 输出:没有返回值,结果直接修改输入数组
nums以表示下一排列。
理解题意:
下一排列是指在所有可能的排列中,找到一个比当前排列稍大但又是最小的排列。 例如,给定 [1, 2, 3],下一个排列是 [1, 3, 2];而给定 [3, 2, 1],下一个排列则是 [1, 2, 3]。
可能涉及的算法知识点:
- 数组与索引操作
- 组合数学
- 逆序查找
- 交换和反转操作
解题思路:
这题的代码不是难点,难点在于思路的理解。
首先我会想,对于 int[] nums = {1, 2, 3, 6, 5, 4}; 这个数组,肉眼可见的,交换6和3的位置,当然是可以做到:新的数组 126354 是 大于 123654的 ,但是他是不是他的下一个排列呢?不是。因为只是做了第一个步骤,也就是从后往前找到了第一个 nums[i] < nums[i+1] 的。
这里就有个问题了,为什么要从后往前搜索呢?
是有点儿神奇,为什么人脑得出来的结果,是首先交换6和3呢?而不是交换2和1
其实是人脑默认已经知道了,应该“优先交换后面的元素” 这个道理了,人脑是知道前面的位数是比较大的。
所以这里默认就是从后往前搜索,找到第一个可以交互的元素。
接着呢?我们又碰到了一个问题,就是126354其实并不是 123654的下一个排列,明显的,我们是知道最终答案的,也就是这个答案应该是124356,而不是126354,所以其实我们应该尽量把比较小的元素,也就是4往前提
怎么找到4呢?再次从右往左遍历一次,找到了第一个比nums[i] 大的元素,这下我们就可以找到4了。
那现在我们有了两个index,一个是6对应的,一个是4对应的
明显的,我们在找到4之后,应该把4和3交换位置,遵循我们尽量把“小”的元素往前提的原则
交换4 和 3 之后,数组变成了 124653,其实这里仍然不是下一个,肉眼可见的 其实124356 才是。
那怎么把 124653 变成 其实124356 ?
其实就来到了解法的最后一步:反转 i+1到n-1 的元素,也就是把653反转成356.
至此思路已经完全对应上:总共有4步
- 1、从右往左找到第一个可以交换的元素的index(当然这里如果不存在的话,直接反转就好了)
- 2、从右往左找到第一个比nums[index]大的元素
- 3、交换它们
- 4、反转后面的元素
没什么技巧,纯碎死记硬背。代码实现时,建议也用123654这个例子来实践,可以加深记忆
-
找到下一个排列:
- 从后向前遍历数组,找到第一个逆序的元素
nums[i],即nums[i] < nums[i + 1]。 - 如果不存在这样的元素,说明当前排列是最大排列,直接反转整个数组。
- 从后向前找第一个比
nums[i]大的元素nums[j],并交换它们。 - 最后,反转下标从
i + 1到数组结束的部分。
- 从后向前遍历数组,找到第一个逆序的元素
-
时间复杂度与空间复杂度:
- 时间复杂度:O(n),其中 n 是数组的长度,因为我们需要遍历数组几次。
- 空间复杂度:O(1),在原数组上进行操作,不需要额外的空间。
代码示例(Java):
public class Solution {
public void nextPermutation(int[] nums) {
int n = nums.length;
// 1. 从后向前找到第一个逆序对
int i = n - 2;
while (i >= 0 && nums[i] >= nums[i + 1]) {
i--;
}
// 2. 如果找到了逆序对
if (i >= 0) {
// 3. 从后向前找到第一个比 nums[i] 大的元素
int j = n - 1;
while (nums[j] <= nums[i]) {
j--;
}
// 4. 交换 nums[i] 和 nums[j]
swap(nums, i, j);
}
// 5. 反转 i + 1 到 n - 1 的部分
reverse(nums, i + 1, n - 1);
}
// 交换两个元素
private void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
// 反转数组中的一部分
private void reverse(int[] nums, int start, int end) {
while (start < end) {
swap(nums, start, end);
start++;
end--;
}
}
}