给出集合 [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"
思路分析:请先查阅 LeetCode 全排列
本算法采取回溯法进行搜索,使用一个变量记录当前搜索到的一个排列,当搜到的位所需要的时候停止搜索,返回排列结果。
class Solution {
public:
vector<bool> useFlag;//使用标记容器
int cnt;//标记个数
bool permuteDFS(string &tempRes, int steps, int n, int k){
if (steps == n){//已经搜索完了一趟
++cnt;
if (cnt == k){//找到了第k个
return true;
}
}
else {
for (int index = 1; index <= n; ++index){//每次都进行穷举
if (useFlag[index] == false){//没使用过
tempRes += ('0' + index);
useFlag[index] = true;//标记使用
if (permuteDFS(tempRes, steps + 1, n, k)){//进行下一步寻找
return true;//如果搜索到了,停止搜索
}
useFlag[index] = false;//重点。标记回未使用
tempRes = string(tempRes, 0, tempRes.size() - 1);
}
}
}
return false;
}
string getPermutation(int n, int k) {
useFlag = vector<bool>(n + 1, false);
cnt = 0;
string tempRes = "";
permuteDFS(tempRes, 0, n, k);//回溯法搜索
return tempRes;
}
};
虽说是回溯法,但是当n,k比较大时,时间复杂度非常大。
经百度发现这是个非常经典的问题,数学术语为“康托展开”。请参考百度 康拓展开
具体实现代码
class Solution {
public:
string getPermutation(int n, int k) {
vector<int> numFactorial = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880 };//用于储存i的阶乘
vector<bool> useFlag(n + 1, false);//标记i是否使用过
string result = "";
k -= 1;//需要减去自身
for (int i = n - 1; i >= 0; --i){//从高位到低位进行寻找
int temp = k / numFactorial[i];//商
k = k % numFactorial[i];//更新为余数
int cnt = 0;
//在还未使用的数中,找第temp大的数
for (int j = 1; j <= n; ++j){
if (useFlag[j] == false){//未使用,计数
++cnt;
}
if (cnt > temp){//找到了
result += to_string(j);
useFlag[j] = true;
break;
}
}
}
return result;
}
};