问题描述:
给出集合[1,2,3,…,n]
,其所有元素共有n!
种排列。
按大小顺序列出所有排列情况,并一一标记,当n = 3
时, 所有排列如下:
"123"
"132"
"213"
"231"
"312"
"321"
给定n
和k
,返回第k
个排列。
说明:
给定n
的范围是[1, 9]
。
给定k
的范围是[1, n!]
。
示例 :
输入: n = 3, k = 3
输出: “213”
问题分析:
题目可以使用回溯法来解决,但是弄不巧就超时了,尴尬。看了大牛的解法,是用的数学思维,现在总结一下人家的数学方法,其实就是一个总结其中的规律的题目。
看一个例子,题目中的例子就可以:
n = 3
,k = 5
所有组合有:
123, 132, 213, 231, 312, 321
很显然,答案为312
。
现在总结规律:
(1)第k
个数字的第1
位数字一定为[1, 2, ... n][k // (n-1)!]
。
解释: 很显然,所有排列一共有n*(n-1)!
种,也可以理解为,每个数字开头的种类组合有(n-1)!
个。所以k // (n-1)!
就可以算出第一位数字,5 // (3-1)! = 2
,所以[1, 2, ... ... n][2] = 3
,即,求出第一位数字了。
(2)现在已经确定了第1
位数字了,接下来要用同样的方法求解第2
位上的数字。但要注意两点:
1、已经使用过的数字,将不再使用。
2、k
值的更新,这个过程其实就是确定一位数字之后,进一步确定在新的范围内是第几个数字。
index = k // (n-1)!
fact = (n-1)!
k = k - index * fact
# 或者
k = k % fact
Python3实现:
import math
class Solution:
def getPermutation(self, n, k):
res = [0] * n
numbers = [x for x in range(1, n + 1)]
k = k - 1
i = 0
while i < n:
fact = math.factorial(n - 1 - i) # 获取阶乘
index = k // fact
res[i] = numbers[index] # 确定一位数字
numbers.pop(index) # 使用过的数字,将不再使用
# 更新 k
k -= index * fact # 或者 k = k % fact
i += 1
return ''.join([str(x) for x in res])
if __name__ == '__main__':
print(Solution().getPermutation(17, 100))
声明: 总结学习,有问题或不妥之处,可以批评指正哦。
题目链接:leetcode-cn.com/problems/permutation-sequence/
代码参考:leetcode.com/problems/permutation-sequence/discuss/195316/Python3-solution-beats-100
代码参考:leetcode.com/problems/permutation-sequence/discuss/196223/Python-O(n)-beats-100-with-explanation