LeetCode #31 找出字典序下一个排列

字典序也就是从头开始,依次比较每个字符在ASCII码中的排位,例如a的排位在b之前,1的排位在3之前,如果字符相同,就继续往后比较。显然,一个只有数字的list要按字典序排列,以[1, 2, 3, 4]为例,应该为:
[1, 2, 3, 4] -> [1, 2, 4, 3] -> [1, 3, 2, 4] -> [1, 3, 4, 2] -> [1, 4, 2, 3] -> [1, 4, 3, 2] -> [2, 1, 3, 4] -> [2, 1, 4, 3] -> [2, 3, 1, 4] ->[…]
从实例总结规律,可以发现以下几点:

  1. 如果list中有“小,大”的组合,那要先找到最靠右的较小数nums[i],再找到其右侧值最小的较大数nums[j],将他们交换,例如: [1, 2, 3, 4]-> [1, 2, 4, 3];

  2. 又如: [1, 3, 4, 2] -> [1, 4, 2, 3],但是这里会注意到3和4交换后,3又被换到了最后一位,这是因为交换后原先3的位置往后所有数字还要重新升序排序,确保这是最靠近原序列的下一个排列,否则,这就不是当前最小的下一个排列。[1, 4, 3, 2] -> [2, 1, 3, 4]也是同理,1与2互换后,[2, 4, 3, 1]还要将后三位的[4, 3, 1]重新升序排列,得到最终结果[2, 1, 3, 4]。

  3. 升序排列可以用双指针直接一趟交换实现,因为较小数和较大数交换后,右侧序列还是降序排列( 因为nums[j-1] <= nums[i] < nums[j] )。

  4. 假如序列是一直降序的,也就不存在“小,大”的组合,这时直接将整个序列升序排列就可以了。

  5. 在找i, j时,i是最靠右的较小数的位置,所以可以从右往左找“小,大”的组合,这样找到的第一个较小数就是最靠右的;而且这样可以确定较小数i之后的数都是降序排列的,那要找其中最小的较大数,也是从右往左找比nums[i]大的数,遇到到的第一个就是最小的较大数。

class Solution:
    def nextPermutation(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        # e.g. 1 3 5 7 8 9 6 4 -------> 1 3 5 7 9 4 6 8 
        # 先找最后一个“小->大”的数对,将其交换,换之后的部分应该再调整为递增的
        l = len(nums)
        a, b = -1, -1
        # 从右往左找到最靠右的较小值
        for i in range(l-1, 0, -1):
            if nums[i] > nums[i-1]:
                a = i-1
                break
        # 在较小值右边的序列中,从右往左找到最小的较大值
        for j in range(l-1, a, -1):
            if nums[j] > nums[a]:
                b = j
                break
        # 存在“小->大”的数对,交换较小值和较大值
        if a>=0 and b>=0:
            temp = nums[a]
            nums[a] = nums[b]
            nums[b] = temp
        # 原来的较小值右侧的数字要重新升序排列
        p,q = a+1, l-1
        while p<q:
            temp = nums[p]
            nums[p] = nums[q]
            nums[q] = temp
            p += 1
            q -= 1
        return nums

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值