内容介绍
给出集合
[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!
完整代码
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个排列。
思路详解
-
阶乘数组初始化
- 定义一个数组
factorial
用于存储从0到n-1的阶乘值。 - 初始化
factorial[0]
为1,然后通过循环计算其余的阶乘值。
- 定义一个数组
-
计算第k个排列
- 由于排列是从0开始的,因此将输入的k减去1以转换为从0开始的索引。
-
分配内存
- 使用
malloc
为结果字符串分配内存,大小为n+1(包括结尾的空字符\0
)。 - 将字符串的最后一个字符设置为空字符,以形成正确的C字符串。
- 使用
-
有效数字标记
- 定义一个数组
valid
来标记哪些数字还未被使用过。初始时,所有数字都是有效的(标记为1)。
- 定义一个数组
-
构建排列
- 使用两个嵌套循环来构建第k个排列。
- 外层循环从1迭代到n,代表排列中的每一个位置。
- 内层循环用于确定当前位置应该放置哪个数字。
-
确定当前数字
- 计算当前位置
i
的阶乘factorial[n - i]
,用于确定当前数字在剩余未使用数字中的顺序。 - 通过计算
order = k / factorial[n - i] + 1
,得到当前数字的顺序。 - 使用一个计数器
order
,通过遍历valid
数组来找到应该放置在当前位置的数字。
- 计算当前位置
-
更新状态
- 将找到的数字转换为字符并放置在结果字符串的对应位置。
- 将
valid
数组中对应的数字标记为0,表示该数字已被使用。 - 更新k的值为
k % factorial[n - i]
,以处理下一个位置的数字。
-
返回结果
- 循环结束后,返回构建好的字符串
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种分配方案,这个函数可以帮助你找到答案。
-
数学证明:在证明某些数学性质时,可能需要考虑所有可能的排列,使用这个函数可以生成所需的排列进行验证。