力扣第六十题——排列序列

内容介绍

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

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

  1. "123"
  2. "132"
  3. "213"
  4. "231"
  5. "312"
  6. "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!

完整代码

 char* getPermutation(int n, int k) {
    int factorial[n];
    factorial[0] = 1;
    for (int i = 1; i < n; ++i) {
        factorial[i] = factorial[i - 1] * i;
    }

    --k;
    char* ans = malloc(n + 1);
    ans[n] = '\0';
    int valid[n + 1];
    for (int i = 0; i <= n; ++i) {
        valid[i] = 1;
    }
    for (int i = 1; i <= n; ++i) {
        int order = k / factorial[n - i] + 1;
        for (int j = 1; j <= n; ++j) {
            order -= valid[j];
            if (!order) {
                ans[i - 1] = j + '0';
                valid[j] = 0;
                break;
            }
        }
        k %= factorial[n - i];
    }
    return ans;
}

思路详解

代码功能

这段代码定义了一个名为getPermutation的函数,它返回一个字符串,表示从1到n的数字组成的所有排列中的第k个排列。

思路详解

  1. 阶乘数组初始化

    • 定义一个数组factorial用于存储从0到n-1的阶乘值。
    • 初始化factorial[0]为1,然后通过循环计算其余的阶乘值。
  2. 计算第k个排列

    • 由于排列是从0开始的,因此将输入的k减去1以转换为从0开始的索引。
  3. 分配内存

    • 使用malloc为结果字符串分配内存,大小为n+1(包括结尾的空字符\0)。
    • 将字符串的最后一个字符设置为空字符,以形成正确的C字符串。
  4. 有效数字标记

    • 定义一个数组valid来标记哪些数字还未被使用过。初始时,所有数字都是有效的(标记为1)。
  5. 构建排列

    • 使用两个嵌套循环来构建第k个排列。
    • 外层循环从1迭代到n,代表排列中的每一个位置。
    • 内层循环用于确定当前位置应该放置哪个数字。
  6. 确定当前数字

    • 计算当前位置i的阶乘factorial[n - i],用于确定当前数字在剩余未使用数字中的顺序。
    • 通过计算order = k / factorial[n - i] + 1,得到当前数字的顺序。
    • 使用一个计数器order,通过遍历valid数组来找到应该放置在当前位置的数字。
  7. 更新状态

    • 将找到的数字转换为字符并放置在结果字符串的对应位置。
    • valid数组中对应的数字标记为0,表示该数字已被使用。
    • 更新k的值为k % factorial[n - i],以处理下一个位置的数字。
  8. 返回结果

    • 循环结束后,返回构建好的字符串ans

总结

这段代码通过以下步骤来生成第k个排列:

  • 预先计算阶乘值以确定每个位置数字的选择范围。
  • 使用阶乘值来确定每个位置应该放置哪个数字。
  • 使用一个标记数组来跟踪哪些数字已被使用。
  • 逐步构建排列,直到所有位置都被填充。

知识点精炼

. 阶乘数组
  • 使用数组factorial存储从0到n-1的阶乘值,便于后续计算排列顺序。
2. 字符串内存分配
  • 使用malloc动态分配内存,并设置字符串结尾的空字符\0
3. 标记数组
  • valid数组用于标记1到n的数字是否已被使用,以避免重复。
4. 排列生成算法
  • 通过除法和取余操作确定每个位置的数字,结合阶乘数组实现。
5. 位置确定
  • 计算当前阶乘值,以确定当前数字在剩余未使用数字中的顺序。
6. 更新状态
  • 每确定一个位置的数字后,更新valid数组,并递减k值。
7. 循环构建
  • 外层循环遍历排列的每个位置,内层循环确定该位置的数字。
8. 结果返回
  • 构建完成后,返回动态分配的字符串,包含第k个排列。
9. 算法效率
  • 时间复杂度:O(n^2),主要由于内层循环遍历未使用数字。
  • 空间复杂度:O(n),用于存储阶乘数组和标记数组。
10. 编程技巧
  • 使用阶乘数组简化排列顺序的计算。
  • 动态内存管理,确保字符串的正确构建。
  • 标记数组的使用,有效管理数字的使用状态。

 

具体应用示例

  • 密码生成:假设有一个密码锁,需要输入一个4位数的密码,每个数字可以是1到6之间的任意数字。使用这个函数,你可以生成所有可能的密码组合,并找到第k个密码。

  • 比赛排位:在一个比赛中,如果需要对参赛者进行排序,并且想要知道如果按照某种规则排序,第k个参赛者是谁,可以使用这个函数来模拟排列。

  • 任务分配:在分配n个任务给k个工人时,如果每个工人只能执行一个任务,并且需要知道第k种分配方案,这个函数可以帮助你找到答案。

  • 数学证明:在证明某些数学性质时,可能需要考虑所有可能的排列,使用这个函数可以生成所需的排列进行验证。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值