Hard
题目描述
集合 [1, 2, 3, …, n] 包含总共 n! 个唯一排列。
按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:
"123"
"132"
"213"
"231"
"312"
"321"
给定 n 和 k,返回第 k 个排列。
示例 1:
输入:n = 3, k = 3
输出:"213"
示例 2:
输入:n = 4, k = 9
输出:"2314"
示例 3:
输入:n = 3, k = 1
输出:"123"
提示:
- 1 <= n <= 9
- 1 <= k <= n!
解题思路
这道题可以用数学方法巧妙求解,避免枚举所有排列。
核心思路: 对于n个数的排列,第一个位置有n种选择,每种选择后面有(n-1)!种排列。因此我们可以通过除法确定第一个数字,通过取余确定在剩余数字中的位置。
具体算法:
- 预计算阶乘值,用于快速计算每个位置的排列数
- 维护一个可选数字列表 [1,2,…,n]
- 对每个位置:
- 计算当前位置每个数字对应的排列组数:(n-1)!
- 通过 (k-1) // (n-1)! 确定当前位置应该选择第几个数字
- 从可选列表中取出该数字,加入结果
- 更新k值:k = (k-1) % (n-1)! + 1
- 继续处理下一个位置
为什么用k-1: 因为排列编号从1开始,而数组索引从0开始,需要转换。
**推荐解法:**数学计算法,时间复杂度O(n²),空间复杂度O(n)。
代码实现
class Solution {
public:
string getPermutation(int n, int k) {
vector<int> factorial(n, 1);
for (int i = 1; i < n; i++) {
factorial[i] = factorial[i - 1] * i;
}
vector<int> nums;
for (int i = 1; i <= n; i++) {
nums.push_back(i);
}
string result = "";
k--; // 转换为0-based索引
for (int i = n; i >= 1; i--) {
int idx = k / factorial[i - 1];
result += to_string(nums[idx]);
nums.erase(nums.begin() + idx);
k %= factorial[i - 1];
}
return result;
}
};
class Solution:
def getPermutation(self, n: int, k: int) -> str:
import math
factorial = [1] * n
for i in range(1, n):
factorial[i] = factorial[i - 1] * i
nums = list(range(1, n + 1))
result = []
k -= 1 # 转换为0-based索引
for i in range(n, 0, -1):
idx = k // factorial[i - 1]
result.append(str(nums.pop(idx)))
k %= factorial[i - 1]
return ''.join(result)
public class Solution {
public string GetPermutation(int n, int k) {
int[] factorial = new int[n];
factorial[0] = 1;
for (int i = 1; i < n; i++) {
factorial[i] = factorial[i - 1] * i;
}
List<int> nums = new List<int>();
for (int i = 1; i <= n; i++) {
nums.Add(i);
}
StringBuilder result = new StringBuilder();
k--; // 转换为0-based索引
for (int i = n; i >= 1; i--) {
int idx = k / factorial[i - 1];
result.Append(nums[idx]);
nums.RemoveAt(idx);
k %= factorial[i - 1];
}
return result.ToString();
}
}
var getPermutation = function(n, k) {
const factorial = new Array(n).fill(1);
for (let i = 1; i < n; i++) {
factorial[i] = factorial[i - 1] * i;
}
const nums = [];
for (let i = 1; i <= n; i++) {
nums.push(i);
}
let result = "";
k--; // 转换为0-based索引
for (let i = n; i >= 1; i--) {
const idx = Math.floor(k / factorial[i - 1]);
result += nums[idx];
nums.splice(idx, 1);
k %= factorial[i - 1];
}
return result;
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n²) | 需要n轮循环,每轮删除元素需要O(n)时间 |
| 空间复杂度 | O(n) | 需要存储阶乘数组和候选数字列表 |
相关题目
. Next Permutation (Medium)
. Permutations (Medium)