跳过正文
  1. leetcode 题解/

31_下一个排列

·141 字·1 分钟

类型:数组

    1. 下一个排列 💛

    https://leetcode-cn.com/problems/next-permutation/

    ❓ 实现获取「下一个排列」的函数,算法需要将给定数字序列重新排列成「字典序」中下一个更大的排列。如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。必须 原地 修改,只允许使用额外常数空间。

    💡 两遍扫描

    我们希望找到一种方法,能够找到一个大于当前序列的新序列,且变大的幅度尽可能小。

    • 我们需要将一个左边的「较小数」与一个右边的「较大数」交换,以能够让当前排列变大,从而得到下一个排列。
    • 同时我们要让这个「较小数」尽量靠右,而「较大数」尽可能小。当交换完成后,「较大数」右边的数需要按照升序重新排列。这样可以在保证新排列大于原来排列的情况下,使变大的幅度尽可能小。

    以排列 [4,5,2,6,3,1] 为例:

    • 我们能找到的符合条件的一对「较小数」与「较大数」的组合为 2 与 3,满足「较小数」尽量靠右,而「较大数」尽可能小。
    • 当我们完成交换后,排列变为 [4,5,3,6,2,1],此时我们可以重排「较小数」右边的序列,序列变为 [4,5,3,1,2,6].

    算法描述如下:

    • 首先从后向前查找第一个满足 a[i] < a[i+1] 的数。a[i] 即为最靠右的「较小数」。
    • 经过上面的查找,我们可以得知 [i + 1, n) 必然是一个下降序列。我们在 [i + 1, n) 中从后向前找到第一个满足 a[i] < a[j] 的元素。a[j] 即是尽可能小的「较大数」。
    • 交换 a[i] 与 a[j]。此时区间 [i + 1, n) 必为降序,我们可以直接使用双指针法反转 [i + 1, n) 使其变为升序。

    如果在步骤 1 中找不到符合要求的「较小数」,说明当前序列已经是最大序列,跳过步骤 2,直接执行步骤 3,即可得到最小的升序序列。

    class Solution:
        def nextPermutation(self, nums: List[int]) -> None:
            i = len(nums) - 2
            while i >= 0 and nums[i] >= nums[i + 1]:
                i -= 1
            if i >= 0:
                j = len(nums) - 1
                while j >= 0 and nums[i] >= nums[j]:
                    j -= 1
                nums[i], nums[j] = nums[j], nums[i]
    
            left, right = i + 1, len(nums) - 1
            while left < right:
                nums[left], nums[right] = nums[right], nums[left]
                left += 1
                right -= 1

    时间复杂度:O(n),空间复杂度:O(1)