Python3刷LeetCode-31.下一个排列

题目描述:
31. 下一个排列
实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。

如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。

必须 原地 修改,只允许使用额外常数空间。

示例 1:

输入:nums = [1,2,3]
输出:[1,3,2]
示例 2:

输入:nums = [3,2,1]
输出:[1,2,3]
示例 3:

输入:nums = [1,1,5]
输出:[1,5,1]
示例 4:

输入:nums = [1]
输出:[1]

题目链接

代码

class Solution:
    def nextPermutation(self, nums: list) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        n = len(nums)
        flag = 0#用来判断是否找到了nums[i]<nums[i+1],如果没有,则说明原排列是降序的,需要将它进行升序排序。
        for i in range(n-2, -1, -1):
            if nums[i] < nums[i+1]:
                flag = 1
                for j in range(n-1, i, -1):
                    if nums[j] > nums[i]:
                        nums[j], nums[i] = nums[i], nums[j]
                        t = i + 1
                        k = n - 1
                        while t < k:
                            nums[t], nums[k] = nums[k], nums[t]
                            t += 1
                            k -= 1
                        break
            if flag == 1:
                break
        if flag == 0:
            nums.sort()


'''class Solution:
    def nextPermutation(self, nums: list) -> None:
        i = len(nums) - 2
        while i >= 0 and nums[i] >= nums[i + 1]:
            i -= 1
        if i >= 0:
            j = len(nums) - 1
            while j >= 0 and nums[i] >= nums[j]:
                j -= 1
            nums[i], nums[j] = nums[j], nums[i]

        left, right = i + 1, len(nums) - 1
        while left < right:
            nums[left], nums[right] = nums[right], nums[left]
            left += 1
            right -= 1'''





if __name__ == '__main__':
    s = Solution()
    nums = [3,2,1]
    s.nextPermutation(nums)
    print(nums)


Ps:注释掉的是官方代码

解题思路:
先举个简单的例子,就用题目的给的示例吧:
[1,2,3]
找他的下一个排列,从数组的末尾开始,看看前边谁比3小,这里是2,所以交换2,3,即得下一个排列。
但是下面这个数组
[2,3,1]
我们先看1,前边没有比它小的,则看下一个3,前边比它小的是2,交换2,3,此时数组变成[3,2,1],并不是他的下一个排列,问题出在交换后,3后边的数组是降序的,需要把它升序后才是下一个排列。这并不是特例,可以证明交换后边的排序一定是降序的。
再看一个例子吧
[1,3,2]
我们交换1,2,数组变成[2,3,1],也需把[3,1]变成[1,3]才是下一个排列。
这是为啥呢,再举个例子吧。
[4,2,0,2,3,2,0]
这个数组,我们从后往前数的话,从0开始,没有比0小的,接下来看2,这时我们能找到比2小的是0(【4,2,0,2,3,2,0】,数组中标黑的两个数),但交换它两是错的,因为我们还有比它小的排列,比如交换[2,3]([4,2,0,2,3,2,0]),所以我们从后往前找小的数时,不是简单的从最后一个数开始,逐个比较,我们找到的这个”小的数“应该是最靠右的,这样才能保证交换后离原排列是最近的。正确的寻找方法是从后往前比较 nums[i] 和 nums[i+1],i从n-2开始(下标从0开始,最后一个数下标是n-1),如果满足nums[i]<nums[i+1],则停止寻找,接下来j从n-1开始,寻找右边第一个比nums[i]大的数,交换nums[i]和nums[j],再把 i 右边降序的数进行升序排列就可以了。
接下来解释一下为什么i右边的数一定是降序的,从我们寻找nums[i]<nums[i+1]可以知道,我们找到的一定是第一个小于右边排列的数,所以i右边一定是降序的。接下来j从后往前寻找,找第一个大于nums[i]的,交换后,nums[i]也满足右边的比它小,左边的比它大(比如[4,2,0,2,3,2,0]这个例子,我们先从后前寻找,第一个满足nums[i]<nums[i+1]的就是【2,3】,于是我们找到了2,可以看到2右边的数组是降序的【3,2,0】,然后我们在从后往前找第一个比2大的数,第一个是3,交换【2,3】,原数组变成[4,2,0,3,2,2,0],可以看到【2,2,0】也是降序的,2右边是【2,0】,左边是【3】,满足右边比它小,左边比它大)。至此,我们就解释了为什么交换后下标i右边的数一定是降序的。
下一个步骤是把下标i后边的数进行升序排序,因为题目要求原地操作,Python的nums.sort()只能进行整个列表的排列,不能进行部分排列,sorted(nums[i:])虽然可以进行部分排列,但他不是原地排列,结果需要赋值给一个新的列表。所以这两个方法都行不通,我们需要自己排列。因为i后边的数组是降序的,所以我们可以使用双指针的方法,左边从i+1开始,右边从n-1开始,交换两边的数,两个指针往中间移动,直到左边指针大于右边指针,结束交换。也举个例子吧,奇数个的数组比如【5,4,3,2,1】,交换【5,1】,【4,2】,左边指针等于右边指针了,退出循环,数组变成【1,2,3,4,5】,偶数的比如【4,3,2,1】,交换【4,1】,【3,2】,左边指针大于右边指针了,退出循环,数组变成【1,2,3,4】。

时间复杂度:O(N),其中 N为给定序列的长度。我们至多只需要扫描两次序列,以及进行一次反转操作。
空间复杂度:O(1),只需要常数的空间存放若干变量。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值