【技巧专题】刷题必备!下一个排列最通俗讲解:只用 O(n) 时间就搞定


当然可以!下面是一篇完整、结构清晰、附实例讲解的 LeetCode 题解博客,适合发布到 CSDN、力扣笔记、博客园或掘金等平台。


LeetCode 31. 下一个排列题解详解 + 实例演示

原题链接:LeetCode 31. Next Permutation

题目描述

实现一个函数,将一个数组重新排列为字典序中下一个更大的排列

如果没有更大的排列(即已经是最大排列),就将其变为最小排列(升序排列)。

你必须原地修改数组(使用常数级额外空间)。


示例

示例 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!),显然不现实。我们需要更优的方法:

整体思路分为三步:

  1. 从后往前找到第一个“降序对” nums[i] < nums[i+1]
  2. 从后往前找到第一个大于 nums[i] 的数 nums[j],交换 ij
  3. 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] = 4nums[4] = 5 → 4 < 5 ×
  • nums[4] = 5nums[3] = 6 → 5 < 6 ×
  • nums[3] = 6nums[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)

总结

这个题是经典的排列题目,技巧性很强,但只要记住“从后向前找到第一个下降对,再从右边找比它大的数并交换,最后反转右半部分”,就能轻松掌握。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值