leetcode专题训练 60. Permutation Sequence

这道题用康托展开的思想进行求解。

康托展开运算

在这里插入图片描述
其中, a i a_i 为整数,并且 0 a i < i , 1 i n 0\le a_i<i,1\le i\le n
a i a_i 表示原数的第i位在当前未出现的元素中是排在第几个
z康托展开的逆运算
既然康托展开是一个双射,那么一定可以通过康托展开值求出原排列,即可以求出n的全排列中第x大排列。

逆康托展开举例

一开始已经提过了,康托展开是一个全排列到一个自然数的双射,因此是可逆的。给出数组(1,2,3,4,5)与位次62,具体过程如下:
由于要算比当前数小的数,所以要将62-1=61
用 61 / 4! = 2余13,说明 a [ 5 ] = 2 a[5]=2 ,说明比首位小的数有2个,所以首位为3。
用 13 / 3! = 2余1,说明 a [ 4 ] = 2 a[4]=2 ,说明在第二位之后小于第二位的数有2个,所以第二位为4。
用 1 / 2! = 0余1,说明 a [ 3 ] = 0 a[3]=0 ,说明在第三位之后没有小于第三位的数,所以第三位为1。
用 1 / 1! = 1余0,说明 a [ 2 ] = 1 a[2]=1 ,说明在第四位之后小于第四位的数有1个,所以第四位为5。
最后一位自然就是剩下的数2。
通过以上分析,所求排列组合为 34152。
(需要注意的是,逆康托展开算出的比当前位小的数的个数,均针对不包含已选过数字的数组)

代码

class Solution:
    def getPermutation(self, n: int, k: int) -> str:
        # 计算阶乘
        fra = 1
        frac = [1]
        for i in range(1, n):
            fra *= i
            frac.append(fra)

		# 计算数字
        nums = [i for i in range(1, n+1)] # 存储没有被选过的数字
        result = []
        k -= 1
        for i in range(n-1, -1, -1):
            tmp = k//frac[i]
            k = k%frac[i]
            result.append(str(nums[tmp]))
            nums.remove(nums[tmp]) # 将已经选过的数字从nums中删除
        return "".join(result) # 要将数字列表转化为字符串

终于放假了哈哈哈哈哈哈,考完试了哈哈哈哈哈哈哈哈哈。我又解放了哈哈哈哈哈哈哈哈哈。

发布了201 篇原创文章 · 获赞 26 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/Ema1997/article/details/104075975