题目:[1]
给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。
按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:
输入: n = 3, k = 3
输出: "213"
输入: n = 4, k = 9
输出: "2314"
抛砖引玉
思路
从1到n转换成从n到1,共有n的阶乘种组合;
分析排列过程:
结合排列的顺序和k的大小,此时可以确定第一个字符
第二个字符:
第三个字符:
...
知道第k中排列或者最终n-x等于1得到最终排列
/**
* @param {number} n
* @param {number} k
* @return {string}
*/
var getPermutation = function (n, k) {
let _result = '',
nums = [], // 存放待排列元素
factorial = 1;
for (let i = 1; i <= n; i++) {
nums.push(i);
factorial = factorial * i;
}
// 计数和索引差1
k= k-1;
while (nums.length > 0) {
factorial = factorial / nums.length; // 确定一个元素后,待排列的组合种类减少为(n-x)的阶乘
let index = parseInt(k / factorial,10); // 当前选择的数字的索引
_result += nums[index]; // 加上当前选的数字
nums.splice(index, 1); // nums删去选的这个数字
k = k % factorial; // 更新 k,
}
return _result;
}
上面的数学法并没有生成具体的组合,都是通过确定元素后能得到的排列组合数来推导出第k个排列
更直观的方法是,枚举每个位置上可能的元素然后记录其对应的种类数,直到枚举到第k
递归选择要拼接的元素:
var getPermutation = function (n, k) {
let used = new Map(),
factorial = 1;
for (let i = 1; i <= n; i++) {
factorial = factorial * i;
}
function helper(temp) {
let len = temp.length;
if (len == n) return temp.join('');
factorial = factorial / (n - len); // 待排列的元素存在的组合数
for (let i = 1; i <= n; i++) {
if (used.has(i)) continue; // 递归时忽略已经排列的元素
if (k > factorial) { // 如果k大于剩余的组合数,则说明如果此位置排列i则排列完成不能有k种组合
k = k - factorial;
continue;
}
temp.push(i); // 满足此位置选择i包含大于等于k中组合
used.set(i, true);
return helper(temp); // 继续选择i后面的元素
}
};
return helper([]);
}