【LeetCode】【31. Next Permutation】(python版)

Description:
Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers.

If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order).

The replacement must be in-place and use only constant extra memory.

Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column.

1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1

题目说明:
在给定数组中数字的全排列结果中,按照字典顺序排列。给定其中一个排列,要求输出字典顺序下的下一个排列形式。如果给定的排列已经是最后一个,那么输出排列中数字的升序排列(其实也就是全排列的第一个)。额外要求空间复杂度为O(1)。

思路

如果给一个排列652731,如何求它的下一个排列。其实也就是说用这6个数字组合一个新的数字,这个数字要比652731大,但是同时也必须是所有组合出的数字中相对于652731增量最小的那个。
我们可以分析一下,这6个数字组成的最大数当然是765321,能不能再找到一个数比652731大,比765321小?首先从最左边开始看,保持‘6’不动,后面五个数还能不能组成更大数?肯定是可以的,最大的为675321,仍然大于652731;那么如果保持‘65’不动,后面四个数呢?最大的为657321,仍然大于652731;如果保持‘652’不动呢?最大可得到652731,刚好就是原给定排列,此时的后三位数是按照降序排列,已经达到了所能得到的最大值。因此想要得到一个稍大的数,最多保持前两个数字‘65’排列顺序不变,对后四位数重排。我们的目标是找到一个比原数字稍大一点的,那么就从最后三位中比第三个数字‘2’大的(‘7’,‘3’)中挑一个最小的,放在第三位,剩下的数字按照升序排列,得到653127。这就是我们想要的下一排列。

把上面的思路整理一下:

  • 从后往前找到第一个升序的数对(a[i],a[i+1])(如果找不到,说明当前已经是能得到的最大排列,只需要将原数组逆置)
  • 从a[i+1:n-1]中找到所有比a[i]大的数字中最小的那个。(由于我们可以确定a[i+1:n-1]部分数字一定是按照降序排列,因此只需要从后往前找到第一个比a[i]大的数字,假设为a[k]。)
  • 交换a[i],a[k]
  • 将a[i+1]及之后的数字按照升序排列。(可以想象,由于a[i+1]>=a[i+2]>= ….. >=a[k]>a[i]>a[k+1]>=……>=a[n-1],因此交换之后的a[i+1:n-1]仍然是降序排列,因此这一步骤可以简化为将a[i+1:n-1]逆置。)
class Solution(object):
    def nextPermutation(self, nums):
        """
        :type nums: List[int]
        :rtype: void Do not return anything, modify nums in-place instead.
        """
        swapindex = -1
        n = len(nums)
        # 找第一个升序对
        for i in range(n-2, -2, -1):
            if nums[i] < nums[i+1]:
                swapindex = i
                break
        if swapindex > -1:
            # 找第一个比swapindex大的数,交换
            for i in range(n - 1, -1, -1):
                if nums[i] > nums[swapindex]:
                    nums[i], nums[swapindex] = nums[swapindex], nums[i]
                    break
        # 后半段逆置
        nums[swapindex+1:] = [nums[i] for i in range(n-1, swapindex, -1)]

之前我们使用过递归方式求全排列,这里既然已经知道了如何根据前一个排列求下一个排列,我们实际上可以用非递归方式实现求全排列。

    def Permutation(self, ss):
        listofss = list(ss)
        listofss.sort()
        out = [listofss[:]]
        self.nextPermutation(listofss)
        # 因为这里可能会有重复字符,所以不能预判会有多少排列,这里采用的是判断是否回到最初排列
        while listofss != out[0]:
            out.append(listofss[:])
            self.nextPermutation(listofss)
        return [''.join(out[i]) for i in range(len(out))]

猜你喜欢

转载自blog.csdn.net/qq_20141867/article/details/81006671