LeetCode 第60题:排列序列

66 阅读3分钟

LeetCode 第60题:排列序列

题目描述

给出集合 [1,2,3,...,n],其所有元素共有 n! 种排列。

按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:

  1. "123"
  2. "132"
  3. "213"
  4. "231"
  5. "312"
  6. "321"

给定 nk,返回第 k 个排列。

难度

困难

题目链接

点击在LeetCode中查看题目

示例

示例 1:

输入:n = 3, k = 3
输出:"213"

示例 2:

输入:n = 4, k = 9
输出:"2314"

示例 3:

输入:n = 3, k = 1
输出:"123"

提示

  • 1 <= n <= 9
  • 1 <= k <= n!

解题思路

方法:数学 + 递归

这道题如果使用回溯法生成所有排列再取第k个会超时,我们需要用数学的方法直接计算第k个排列。

关键点:

  1. 每个位置的数字可以通过 k 和阶乘数推导出来
  2. 使用阶乘系统来定位每一位的数字
  3. 维护一个数字列表,记录可用的数字
  4. 每确定一位就将对应数字从列表中移除

具体步骤:

  1. 计算n的阶乘数组
  2. 将k减1(因为排列从1开始计数)
  3. 对于每一位(从左到右):
    • 计算当前位的数字应该是剩余数字中的第几个
    • 将这个数字加入结果
    • 从可用数字列表中移除这个数字
    • 更新k值
  4. 返回最终的字符串

时间复杂度:O(n²),主要是移除数字的操作 空间复杂度:O(n),需要存储阶乘数组和可用数字列表

代码实现

C# 实现

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> numbers = new List<int>();
        for (int i = 1; i <= n; i++) {
            numbers.Add(i);
        }
        
        k--; // 将k转换为0-based索引
        StringBuilder sb = new StringBuilder();
        
        // 对于每一位
        for (int i = n - 1; i >= 0; i--) {
            int index = k / factorial[i];
            k %= factorial[i];
            
            // 添加当前位的数字
            sb.Append(numbers[index]);
            numbers.RemoveAt(index);
        }
        
        return sb.ToString();
    }
}

执行结果

  • 执行用时:84 ms
  • 内存消耗:36.5 MB

代码亮点

  1. 🎯 使用数学方法直接计算,避免生成所有排列
  2. 💡 预计算阶乘数组,提高效率
  3. 🔍 使用List来维护可用数字,方便删除操作
  4. 🎨 使用StringBuilder构建结果字符串

常见错误分析

  1. 🚫 没有将k转换为0-based索引
  2. 🚫 阶乘计算错误
  3. 🚫 数字删除顺序错误
  4. 🚫 没有正确处理边界情况

图解思路

示例分析(n=4, k=9)

初始状态:
可用数字:[1,2,3,4]
k = 8 (转换为0-based)
阶乘数组:[1,1,2,6]

第1位:
k = 8, factorial[3] = 6
index = 8 / 6 = 1
选择数字2,剩余[1,3,4]
k = 8 % 6 = 2

第2位:
k = 2, factorial[2] = 2
index = 2 / 2 = 1
选择数字3,剩余[1,4]
k = 2 % 2 = 0

第3位:
k = 0, factorial[1] = 1
index = 0 / 1 = 0
选择数字1,剩余[4]
k = 0 % 1 = 0

第4位:
k = 0, factorial[0] = 1
index = 0 / 1 = 0
选择数字4,剩余[]

最终结果:"2314"

解法对比

解法时间复杂度空间复杂度优点缺点
回溯法O(n! * n)O(n)思路简单效率极低
数学法O(n²)O(n)效率高实现复杂

相关题目