题目
给出集合 [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”
解题思路
数学方法,一位一位来计算,可通过n和k计算出当前位上的数字。
因为是按从小到大顺序的排列,所以取数原则是从小到大。
例如:当前求第0位的数字,则剩下的位数为n-1位,剩下数字共有 (n-1)!
种排列,则要使得当前为第k种排列,那么第0位应该使用第 k/(n-1)!
种选取方法,选取后,k = k%(n-1)!
,当k为1时,也就是剩下序列为顺序序列时,直接把剩余所有未选元素按顺序加入即可。
比如示例2种,取第一位时,(n-1)! = 3! = 6;则第一位选用 9/6 = 1,(从0开始),也就是未选取元素中的第二种。当前未选取元素为1,2,3,4
,选取第二个,即为2,然后置k = 9%6=3,重复以上过程。
需要注意的是,若 k = k%(n-i)!
结果为0,则当前选取元素退1,置 k=(n-i)!
原因:此时正好使用 后面n-1个元素的全排列即可。
代码
class Solution {
public:
string getPermutation(int n, int k) {
if( n == 1) return "1";
vector<int> factorial(n+1); //阶乘
factorial[0] =1 ;
for(int i=1;i<=n;i++)
{
factorial[i] = factorial[i-1]*i;
}
vector<int> sig(n+1,0); //标志用过的
string res = "";
int temp = k;
for(int i = 1;i<=n;i++)
{
if(temp == 1)
{
for(int j = 1;j<=n;j++)
{
if(sig[j] == 0)
{
res += to_string(j);
}
}
break;
}
int pos = temp/factorial[n-i]; //第几个
temp -= pos*factorial[n-i];
if( temp == 0 )
{
pos--;
temp = factorial[n-i];
}
for(int j = 1;j<=n;j++)
{
if( sig[j] ==0 )
{
if(pos == 0 )
{
res += to_string(j);
sig[j] = 1;
break;
}
pos--;
}
}
}
return res;
}
};