目录
当然可以!下面是一篇完整、结构清晰、附实例讲解的 LeetCode 题解博客,适合发布到 CSDN、力扣笔记、博客园或掘金等平台。
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]
思路分析
这道题的目标是:找出比当前数组排列大的“下一个字典序排列”。
如果我们直接全排列枚举再找下一个,时间复杂度是 O(n!)
,显然不现实。我们需要更优的方法:
整体思路分为三步:
- 从后往前找到第一个“降序对”
nums[i] < nums[i+1]
- 从后往前找到第一个大于
nums[i]
的数nums[j]
,交换i
和j
- 将
nums[i+1:]
的部分反转(从大到小变成从小到大)
完整代码(Java)
class Solution {
public void nextPermutation(int[] nums) {
int n = nums.length;
// 第一步:从右向左找第一个 nums[i] < nums[i + 1]
int i = n - 2;
while (i >= 0 && nums[i] >= nums[i + 1]) {
i--;
}
// 第二步:如果找到了这样的 i,找右边比它大的最小值并交换
if (i >= 0) {
int j = n - 1;
while (nums[j] <= nums[i]) {
j--;
}
swap(nums, i, j);
}
// 第三步:将 i+1 到末尾的数反转
reverse(nums, i + 1, n - 1);
}
private void swap(int[] nums, int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
private void reverse(int[] nums, int left, int right) {
while (left < right) {
swap(nums, left++, right--);
}
}
}
实例讲解:手把手推演
输入:[1, 2, 3, 6, 5, 4]
第一步:找到第一个“下降对”
从右往左看,发现:
nums[5] = 4
,nums[4] = 5
→ 4 < 5 ×nums[4] = 5
,nums[3] = 6
→ 5 < 6 ×nums[3] = 6
,nums[2] = 3
→ √
找到:i = 2
第二步:从右向左找第一个大于 nums[i] = 3 的数
从末尾开始:
nums[5] = 4
> 3
找到 j = 5
,交换 nums[2]
和 nums[5]
:
得到:[1, 2, 4, 6, 5, 3]
第三步:反转 i+1 到末尾的部分
反转 [6, 5, 3]
→ [3, 5, 6]
最终结果:[1, 2, 4, 3, 5, 6]
这就是比 [1, 2, 3, 6, 5, 4]
更大的下一个排列!
特殊情况:整个序列已经是最大排列
比如:[3, 2, 1]
第一步找不到 i
,说明已经是字典序最大排列。
直接跳到第三步,把整个数组反转成最小排列 [1, 2, 3]
。
时间复杂度
- 最多扫描两次数组,时间复杂度:
O(n)
- 原地交换与反转,空间复杂度:
O(1)
总结
这个题是经典的排列题目,技巧性很强,但只要记住“从后向前找到第一个下降对,再从右边找比它大的数并交换,最后反转右半部分”,就能轻松掌握。