1. 程式人生 > >Leetcode 60:第k個排列(超詳細的解法!!!)

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)
  • 2 + p e r m u t a t i o n ( 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)
  • 4 + p e r m u t a t i o n ( 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) 中,並且是 p e r m u t a t i o n ( 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) 可以分為如下三種情況

  • 1 + p e r m u t a t i o n ( 2 , 4 ) 1+permutation(2,4)
  • 2 + p e r m u t a t i o n ( 1 , 4 ) 2+permutation(1,4)
  • 4 + p e r m u t a t i o n ( 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) 中,並且是 p e r m u t a t i o n ( 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)!)

邊界問題也非常容易,我們只要考慮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

如有問題,希望大家指出!!!