下一个排列¶
整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
例如,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
完事儿。
子问题¶
综上所述,寻找下一个排列可以分解为两个子任务:
- 从后向前寻找一个非降序的子数组,然后把子数组的第一位和后面比它大的那些元素中 最小的那一个 交换。
- 当然,如果没有找到非降序子数组,就说明整个数组是降序的,已经是最大的排列了
- 这时候按照题设,只需要反转数组即可
- 交换之后,把剩余的元素排列为升序(实际上只需要反转数组)。
代码¶
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
创建日期: 2025-04-17 21:54:37