Skip to main content

31.下一个排列

标签: array, two-pointers

难度: Medium

通过率: 41.78%

原题链接: https://leetcode.com/problems/next-permutation/description/

题目描述

排列是整数数组的一个排列组合,表示为一个线性序列。例如,对于数组 [1,2,3][1,2,3],所有可能的排列有 [1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1][1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]。给定一个整数数组 numsnums,其下一个排列是指在所有可能的排列中,字典序仅次于当前排列的下一个组合。如果不存在下一个字典序更大的排列,则将数组重新排序为最小的字典序(即升序)。例如:对于数组 [1,2,3][1,2,3],下一个排列是 [1,3,2][1,3,2];对于数组 [3,2,1][3,2,1],下一个排列是 [1,2,3][1,2,3]。这个问题要求在原地修改数组,并且只使用常数级别的额外空间。

解题思路

为求出给定数组的下一个排列,我们可以遵循以下步骤:

  1. 从右往左找到第一个相邻升序对:即找到最大索引 kk,使得 nums[k] < nums[k+1]。如果不存在这样的索引,说明当前排列是最大的字典序排列,我们需要将其逆序为最小字典序排列。

  2. 如果找到了这样的索引 kk,从右往左找到第一个大于 nums[k]nums[k] 的元素:即找到最大索引 ll,满足 nums[k] < nums[l]

  3. 交换nums[k]nums[l]

  4. 反转nums[k+1]到数组末尾的元素,使其成为升序以得到下一个字典序最小的排列。

代码实现

def nextPermutation(nums):
# 从后向前找到第一个升序对
k = len(nums) - 2
while k >= 0 and nums[k] >= nums[k + 1]:
k -= 1

if k == -1: # 如果没找到升序对,直接反转整个数组
nums.reverse()
else:
# 找到第一个比nums[k]大的元素从后往前
l = len(nums) - 1
while nums[l] <= nums[k]:
l -= 1
# 交换
nums[k], nums[l] = nums[l], nums[k]
# 反转后面的元素
nums[k + 1:] = nums[k + 1:][::-1]

复杂度分析

时间复杂度:O(n)O(n),其中 nn 是数组的长度。最坏情况下左右指针各遍历一次数组。 空间复杂度:$O(1)$,我们只使用了常数级别的额外空间。}