题目描述
集合 $[1, 2, 3, …, n]$ 总共有 $n!$ 个不同的排列。
当 $n=3$ 时,将全排列排好序,会得到如下结果:
- “123”
- “132”
- “213”
- “231”
- “312”
- “321”
给出 $n$ 和 $k$, 请返回第 $k^{th}$ 个排列。
样例1
输入:n = 3, k = 3
输出:"213"
样例2
输入:n = 4, k = 9
输出:"2314"
算法
(计数) $O(n^2)$
做法:
- 从高位到低位依次考虑每一位;
- 对于每一位,从小到大依次枚举未使用过的数,确定当前位是几;
为了便于理解,我们这里给出一个例子的具体操作:$n = 4, k = 14$。
首先我们将所有排列按首位分组:
- 1 + (2, 3, 4的全排列)
- 2 + (1, 3, 4的全排列)
- 3 + (1, 2, 4的全排列)
- 4 + (2, 3, 4的全排列)
接下来我们确定第 $k = 14$ 个排列在哪一组中。每组的排列个数是 $3! = 6$ 个,所以第14个排列在第3组中,所以首位已经可以确定,是3。
然后我们再将第3组的排列继续分组:
- 31 + (2, 4的全排列)
- 32 + (1, 4的全排列)
- 34 + (1, 2的全排列)
接下来我们判断第 $k = 14$ 个排列在哪个小组中。我们先求第 $14$ 个排列在第三组中排第几,由于前两组每组有6个排列,所以第14个排列在第3组排第 $14 - 6 * 2 = 2$。
在第三组中每个小组的排列个数是 $2! = 2$个,所以第 $k$ 个排列在第1个小组,所以可以确定它的第二位数字是1。
依次类推,可以推出第14个排列是 3142。
时间复杂度分析:两重循环,所以时间复杂度是 $O(n^2)$。
C++ 代码
class Solution {
public:
string getPermutation(int n, int k) {
string res;
vector<bool> st(n);
for (int i = 0; i < n; i ++ ) {
int f = 1;
for (int j = 1; j < n - i; j ++ ) f *= j;
for (int j = 0; j < n; j ++ ) {
if (!st[j]) {
if (k <= f) {
res += to_string(j + 1);
st[j] = true;
break;
}
k -= f;
}
}
}
return res;
}
};
其实还是O(n^2), 删除操作O(n)
卧槽,我发现我O(n)了。
根据yxc代码改编:
不错
学长 我想问一下 此程序中f表达的意思是什么 没看懂。。。
f 是求 $(n - i - 1)!$,表示按第 $i$ 位分组之后,每一组中排列的个数。从第 $n - 1$ 位到第 $i$ 位都确定以后,还剩下 $n - i - 1$ 个数,所以每组的排列个数就是 $(n - i - 1)!$。