跳转至

下一个排列

整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。

例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。

整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。

更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。

如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。

算法

这是一个C++标准库算法:std::next_permutation

从后往前

为了寻找下一个排列,我们可从后往前遍历数组。

这是因为,一般情况下,(如果存在)下一个更大排列,那么它和现有的排列的前面几个元素应该是一致的

所以我们最终的操作一定是保持前面几个元素不变重新排列后面的一个子数组

例如:

1, 2, 6, 3, 5, 4, 1 

它的下一个排列是:

1, 2, 6, 4, 1, 3, 5

所以我们的任务实际上就是保持最开始的1,2,6不变,重新排列之后的3,5,4,1

重新排列

从后往前看:

  • 首先是1,单个元素的数组肯定没法找到下一个排列
  • 其次是4,1,它已经是最大的排列了(降序排列),没有下一个排列
  • 而后是5,4,1,依旧不存在下一个排列
  • 最后是3,5,4,1,这不是一个降序排列。所以存在下一个排列。

现在需要重新排列3,5,4,1,使得它变得更大,并且和原来的排列差异尽可能小。

所以3肯定要和比它大的那些元素中 最小的那一个 交换。

其实也就是3肯定要换成4开头。其余的元素,需要排列为升序,这样就是下一个排列啦!

3, 5, 3, 1

值得注意的是,按照我们的交换规则,交换之后其余的元素一定排列为降序。所以我们实际上只需要做数组反转即可。

3, 1, 3, 5

完事儿。

子问题

综上所述,寻找下一个排列可以分解为两个子任务:

  1. 从后向前寻找一个非降序的子数组,然后把子数组的第一位和后面比它大的那些元素中 最小的那一个 交换。
    • 当然,如果没有找到非降序子数组,就说明整个数组是降序的,已经是最大的排列了
    • 这时候按照题设,只需要反转数组即可
  2. 交换之后,把剩余的元素排列为升序(实际上只需要反转数组)。

代码

def nextPermutation(self, nums: List[int]) -> None:
    n = len(nums)
    i = n - 2
    # 从后往前,寻找第一个非降序的位置
    while i >= 0 and (nums[i] >= nums[i + 1]):
        i -= 1
    # 如果没找到,i就是-1,跳过下面的交换逻辑
    # 如果找到了就进入下面的交换逻辑
    if i >= 0:
        j = n - 1
        while j >= 0 and (nums[i] >= nums[j]):
            j -= 1
        nums[i], nums[j] = nums[j], nums[i]
    # 双指针反转数组
    left, right = i + 1, n - 1
    while left < right:
        nums[left], nums[right] = nums[right], nums[left]
        left += 1
        right -= 1

最后更新: 2025-05-29 02:42:18
创建日期: 2025-04-17 21:54:37

评论