60. 第k个排序(Python)

更多精彩内容,请关注【力扣中等题】。

题目

难度:★★★★☆
类型:数组
方法:递归、数学

原题链接

给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:

"123"
"132"
"213"
"231"
"312"
"321"

给定 n 和 k,返回第 k 个排列。

说明:

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

示例

示例 1

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

示例 1

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

解答

关于如何通过回溯法或递归法实现全排列可移步全排列。

我们观察这样一个现象,对于从1到n的数组,其全排列有n!个可能性,这些组合是如何排列的呢?

我们思考这样一个问题,一天有246060=86400秒,但是如果让你计算第16589秒是几时几分几秒,你会如何计算呢?

正常情况下我们会首先计算几点,看看这个时刻落在24小时或24个片段中的哪一个,通过将时刻除以3600秒我们获得了4余2189秒,说明是4点,我们继续查看是几分,通过把余数2189除以60秒,我们获得了36余29,那么我们就很清楚的得到了最后的时间:4时36分29秒。

这个问题与之类似。我们列出来[1, 2, 3, 4]的全排列,寻找第9个全排列:

1234
1243
1324
1342
1423
1432

2134
2143
2314
2341
2413
2431

3124
3142
3214
3241
3412
3421

4123
4132
4213
4231
4312
4321

观察发现,总共有4×3×2×1个可能,按照第一个元素可以分为四段,分别以1,2,3和4开头,且以1开头的情况下又有3×2个可能,又可以分为3段,分别以剩余三个数字开头……因此,我们可以首先用9除以6取整来判断第9个排列在第几段,得知落在了第二段,因此第一个元素是2。余数3可以用于后续元素的判断,这时还没有使用的元素是1,3和4,用余数3除以子段的个数2可以得到1余1,那么第二个元素就是3,这时前两个元素“23”就确定下来了,同样的方法,一直计算下去,就可以获得每一位的数值。

在编码过程中需要注意的是,我们用于计算商和余数所用的除数是一个阶乘,这个阶乘的计算与输入和当前情况有关,需要准确计算。

import math
class Solution:
    def getPermutation(self, n: int, k: int) -> str:
        visited = [0 for _ in range(n)]                     # e.g. [0, 0, 0]
        fact = [math.factorial(n-i-1) for i in range(n)]    # e.g. [2!, 1!, 0!]
        ans = ''
        k -= 1
        for i in range(n):
            t = k / fact[i]
            for j in range(n):
                if not visited[j]:
                    if t == 0:
                        break
                    t -= 1
            ans += str(j+1)
            k %= fact[i]
            visited[j] = 1
        return ans

如有疑问或建议,欢迎评论区留言~

你可能感兴趣的:(60. 第k个排序(Python))