题目:实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
注意:必须 原地 修改,只允许使用额外常数空间。
思路:分析下一个序列的形成过程,具体看代码注释。
复杂度:时间复杂度O(n) 空间复杂度O(1)
PS:得到字典序的下一个排列的方法是寻找小数(升序的倒数第二个)和大数(降序中逆序查找第一个大于小数的),进行交换后将降序部分逆序得到的结果。
'''
题目:实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
注意:必须 原地 修改,只允许使用额外常数空间。
思路:
分析理论:
⭐ 下一个排列的理解 如 1234 → 1243 → 1324 → 1342 → 1423 → 1432 ||→ 2134 → 2143 → 2314 → 2341 → 2413 → 2431 → ……………… → 4321
⭐ ① 如何得到更大的排列:要想数更大,则需要将 后边的大数 与 前边的小数 进行交换即可。但是虽然4321比1234大,但是不可作为其下一个,因为中间还是有很多的,那么如何得到增幅更小的排列呢
1234 → 1243 → 1342 → 1432
⭐ ② 如何得到增幅较小的排列: 从后向前进行寻找"大数"(该大数要尽可能小)以及对应该大数的"小数",交换后需要将大数后的所有数重置为升序,则得到置换后的最小的排列
如 ① 在 1243 → 1342之后应该将42重新排序为24即从最小开始遍历
1234 → 1243 → 1324 → 1342 → 1423 → 1432
如何实践:
⭐ 如何得到“大数”,“小数”:
观察 1234 → 1243 → 1342 → 1432
大数 4 3 4
小数 3 2 3
我们会发现一个规律,大数的取值都是在该序列中降序部分取值的,小数取值为在该序列中升序部分的倒数第二位。如果大数要满足尽可能的小,则需要在降序序列中从后往前取值,第一个大于小数的则为大数。
也就是说 对于序列A[1,……,i,j,……,n],A[1,……,i,j]是升序则A[i]是小数。A[j,……,n]是降序,大数在该范围取值,从A[n]开始向前遍历,第一个大于A[i]的即为大数,我们记为A[k]。
⭐ 如何得到增幅较小的排列:
交换大小数后我们得到的序列为A[1,……,k,j,…,i,…,n],因为A[k]是第一个大于A[i]的,所以A[k+1,…,n]是小于A[i]的,故交换后的A[j,……,n]也是降序的(可能有A[k]=A[k-1]的情况),
所以我们可以直接将交换后的A[j,……,n]进行逆序从而实现升序。
如1243 → 1342 我们得到大小数交换后的结果为1342 此时我们将42进行逆序,则得到的1324为下一个排列。
复杂度:时间复杂度O(n) 空间复杂度O(1)
'''
def nextPermutation(nums):
n = len(nums)
i, j, k = 0, 1, n-1
# 当输入的是空或者只有一个数的列表时
if n <= 1:
return nums
# 找最小数的位置
for ind in range(n-1):
if nums[ind] < nums[ind+1]:
i, j = ind, ind+1
# 找到的nums[i]<nums[j] 但如果是 [4,3,2,1] nums[i]>nums[j]
if nums[i]>nums[j]:
nums.reverse()
return nums
# 找最大数的位置
for ind in range(n-1, j-1, -1):
if nums[ind] > nums[i]:
k = ind
break
# 交换 1342
nums[i], nums[k] = nums[k], nums[i]
# 将逆序 nums[j,……,n]逆序
for ind in range((n-j)//2):
nums[ind+j], nums[n-1-ind] = nums[n-1-ind], nums[ind+j]
return nums