LC31(HOT100). 下一个排列(实现字典序-中等)

31. 下一个排列

题目:实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
       注意:必须 原地 修改,只允许使用额外常数空间。
思路:分析下一个序列的形成过程,具体看代码注释。
复杂度:时间复杂度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

路虽远,行则将至。事虽难,做则必成 。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值