Leetcode 60:第k个排列(超详细的解法!!!)

给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。

按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:

  1. "123"
  2. "132"
  3. "213"
  4. "231"
  5. "312"
  6. "321"

给定 nk,返回第 k 个排列。

说明:

  • 给定 n 的范围是 [1, 9]。
  • 给定 k 的范围是[1, n!]。

示例 1:

输入: n = 3, k = 3
输出: "213"

示例 2:

输入: n = 4, k = 9
输出: "2314"

解题思路

首先想到的最简单的做法就是将所有的排列情况列出来,然后挑选出第k-1个即可。这里有一个trick,我们可以不将所有的情况列出,而是通过生成器遍历

class Solution:
    def getPermutation(self, n, k):
        """
        :type n: int
        :type k: int
        :rtype: str
        """
        for i, per_list in enumerate(itertools.permutations([i + 1 for i in range(n)])):
            if i == k - 1:
                return ''.join([str(i) for i in per_list])

如果像下面这样写的话就会超时。

class Solution:
    def getPermutation(self, n, k):
        """
        :type n: int
        :type k: int
        :rtype: str
        """
        per_list = list(itertools.permutations([i + 1 for i in range(n)]))
        return ''.join([str(i) for i in per_list[k-1]])

但是这种写法显然很烂,我们可以通过递归的方法解决这个问题。对于n=4时,我们实际上会产生如下四种情况:

  • 1 + p e r m u t a t i o n ( 2 , 3 , 4 ) 1+permutation(2,3,4) 1+permutation(2,3,4)
  • 2 + p e r m u t a t i o n ( 1 , 3 , 4 ) 2+permutation(1,3,4) 2+permutation(1,3,4)
  • 3 + p e r m u t a t i o n ( 2 , 1 , 4 ) 3+permutation(2,1,4) 3+permutation(2,1,4)
  • 4 + p e r m u t a t i o n ( 2 , 3 , 1 ) 4+permutation(2,3,1) 4+permutation(2,3,1)

我们知道对于每种情况都会有(n-1)!种子集,对于上列来说就是6种子集。如果k=14的话,我们知道它一定在 3 + p e r m u t a t i o n ( 2 , 1 , 4 ) 3+permutation(2,1,4) 3+permutation(2,1,4)中,并且是 p e r m u t a t i o n ( 2 , 1 , 4 ) permutation(2,1,4) permutation(2,1,4)的第二个子集(14-12=2,我们是按照例子中的index考虑,也就是数列从1开始。如果按照从0开始的话,我们输入就要变成14-1=13)。我们看 p e r m u t a t i o n ( 2 , 1 , 4 ) permutation(2,1,4) permutation(2,1,4)可以分为如下三种情况

  • 1 + p e r m u t a t i o n ( 2 , 4 ) 1+permutation(2,4) 1+permutation(2,4)
  • 2 + p e r m u t a t i o n ( 1 , 4 ) 2+permutation(1,4) 2+permutation(1,4)
  • 4 + p e r m u t a t i o n ( 2 , 1 ) 4+permutation(2,1) 4+permutation(2,1)

我们知道对于每种情况都会有(n-2)!种子集,对于上列来说就是2种子集。我们知道它一定是在 1 + p e r m u t a t i o n ( 2 , 4 ) 1+permutation(2,4) 1+permutation(2,4)中,并且是 p e r m u t a t i o n ( 2 , 4 ) permutation(2,4) permutation(2,4)的第二个子集,也就是(4,2),所以最后的结果就是"3142"。我们可以非常轻松的得到如下递推公式

  • f ( n , k ) = n _ l i s t [ k / ( n − 1 ) ! ] + f ( n − 1 , k % ( n − 1 ) ! ) f(n,k)=n\_list[k/(n-1)!] + f(n-1,k\%(n-1)!) f(n,k)=n_list[k/(n1)!]+f(n1,k%(n1)!)

边界问题也非常容易,我们只要考虑n==1时,返回n_list[0]即可。

class Solution:
    def getPermutation(self, n, k):
        """
        :type n: int
        :type k: int
        :rtype: str
        """
        factorials = [1]*(n+1)
        for i in range(1, n+1):
            factorials[i] = factorials[i-1]*i
            
        n_list = [i for i in range(1, n+1)]
        return self.helper(n, k-1, n_list, factorials)
    
    def helper(self, n, k, n_list, factorials):
        if n == 1:
            return str(n_list[0])
        
        m = k // factorials[n-1]
        k %= factorials[n-1]     
        res = str(n_list[m])
        n_list.remove(n_list[m])
        res += self.helper(n-1, k, n_list, factorials)
        return res

这个问题通过迭代也可以非常快的实现。

class Solution:
    def getPermutation(self, n, k):
        """
        :type n: int
        :type k: int
        :rtype: str
        """
        factorials = [1]*(n+1)
        for i in range(1, n+1):
            factorials[i] = factorials[i-1]*i
            
        n_list = [i for i in range(1, n+1)]

        k -= 1
        res = ''
        for i in range(1, n+1):
            m = k // factorials[n-i]
            k %= factorials[n-i]     
            res += str(n_list[m])
            n_list.remove(n_list[m])

        return res

reference:

https://leetcode.com/problems/permutation-sequence/discuss/22507/“Explain-like-I’m-five”-Java-Solution-in-O(n)

我将该问题的其他语言版本添加到了我的GitHub Leetcode

如有问题,希望大家指出!!!

你可能感兴趣的:(Problems,leetcode解题指南)