题意
给出集合 [1,2,3,…,n],其所有元素共有n!种排列。给定n和k,返回第k个按字典序排序的全排列。
分析
1.从左边到右边依次考虑每一位。
2.对于每一位,从小到大依次枚举没有确定位置的数,然后确定当前位置的数值。
举个例子,这里n=4, k=11。
首先我们将所有排列按首位分组:
1. 1 + (2,3,4的全排列)
2. 2 + (1,3,4的全排列)
3. 3 + (1,2,4的全排列)
4. 4 + (1,2,3的全排列)
接下来确定第k=11个排队到底在哪个分组中。每个分组的排列个数是3的阶乘,也就是6个,因此第11个排列是在
第2组中,所以首位可以确定是2。
接着再将第2组的全排列分组:
1. 21 + (3,4的全排列)
2. 23 + (1,4的全排列)
3. 24 + (1,3的全排列)
这里先求第11个排列在第二组中排第几,由于前面只有一个小组,因此第11个排列在第2组中排第11-6*1=5。
而在第二个组中每个小组的全排列个数是2的阶乘,也就是2,因此第11个排列是在第2组中的第3小组,这样子
就可以确定第11个排序的第二个数组是4。
以此类推,第11个排列是2413。
该算法的时间复杂度为O(n * n),空间复杂度为O(n)。
代码
c++
class Solution {
public:
string getPermutation(int n, int k) {
string ans;
//st数组记录哪个数已经确定位置
vector<bool> st(n + 1);
//最外面这层循环每执行一次就会在左边确定一个数
for (int i = 1; i <= n; i++) {
//f用于存储全排列个数
int f = 1;
//等下会确定第i个数,那么剩下的n-i个数一共可以构成(n-i)!个全排列
for (int j = n - i; j >= 1; j--) f *= j;
//从1~n这n个数中选出一个数,放在位置i
for (int j = 1; j <= n; j++) {
//如果某个数还没确定位置
if (!st[j]) {
//确定所在的全排列分组
if (k <= f) {
ans += to_string(j);
st[j] = 1;//数字j确定放在位置i上
break;
}
//查看下一个全排列分组
k -= f;
}
}
}
return ans;
}
};