题目描述
给出集合 [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”
示例 2:
输入: n = 4, k = 9
输出: “2314”
分析
题目理解起来很简单,就是输出第k个排列,最简单的方法就是利用递归或者非递归的形式生成所有的排列,然后选择第k个,这样的时间复杂度为
O
(
n
!
)
O(n!)
O(n!),时间有点长,那么,我们是否可以通过其他的方式直接获取到第k个排列呢?答案是有的。
我们先通过逐个确认排列里的每一个数来得到最终的排列。
首先我们设排列为
a
1
,
a
2
,
⋯
,
a
n
a_1,a_2,\cdots,a_n
a1,a2,⋯,an,我们来确认第一个
a
1
a_1
a1。
根据排列的数量,我们可以很快的算出:
以1开始的排列有
(
n
−
1
)
!
(n-1)!
(n−1)!个,以2开始的排列有
(
n
−
1
)
!
(n-1)!
(n−1)!个,
⋯
\cdots
⋯,以n开始的排列有
(
n
−
1
)
!
(n-1)!
(n−1)!个。所以我们可以推断出当
(
i
−
1
)
(
n
−
1
)
!
<
k
≤
i
(
n
−
1
)
!
(i-1)(n-1)!<k\leq i(n-1)!
(i−1)(n−1)!<k≤i(n−1)!时,
a
1
=
i
a_1=i
a1=i。我们可以通过公式计算出
a
1
=
⌊
k
−
1
(
n
−
1
)
!
⌋
+
1
a_1=\lfloor\frac{k-1}{(n-1)!}\rfloor+1
a1=⌊(n−1)!k−1⌋+1,在完成这一步以后,我们就可以继续往下做了,此时待放入排列里的数就变成
[
1
,
n
]
\
a
1
[1,n]\backslash a_1
[1,n]\a1,k变成了k’,
k
′
=
(
k
−
1
)
m
o
d
(
n
−
1
)
!
+
1
k'=(k-1) mod(n-1)!+1
k′=(k−1)mod(n−1)!+1。这样就变成从n-1个数里选择第k’个排列的子问题了,这样就可以进行递归或者循环下去。时间复杂度为
O
(
n
2
)
O(n^2)
O(n2)
代码
class Solution {
public:
string getPermutation(int n, int k) {
vector<int> factorial(n);
factorial[0] = 1;
for (int i = 1; i < n; ++i) {
factorial[i] = factorial[i - 1] * i;
}
--k;
string ans;
vector<int> valid(n + 1, 1);
for (int i = 1; i <= n; ++i) {
int order = k / factorial[n - i] + 1;
for (int j = 1; j <= n; ++j) {
order -= valid[j];
if (!order) {
ans += (j + '0');
valid[j] = 0;
break;
}
}
k %= factorial[n - i];
}
return ans;
}
};