LeetCode 60. 排列序列【数学,逆康托展开】困难

news/2024/7/7 20:49:00

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,还会用多种编程语言实现题解,涉及到通用解法时更将归纳总结出相应的算法模板。

为了方便在PC上运行调试、分享代码文件,我还建立了相关的仓库:https://github.com/memcpy0/LeetCode-Conquest。在这一仓库中,你不仅可以看到LeetCode原题链接、题解代码、题解文章链接、同类题目归纳、通用解法总结等,还可以看到原题出现频率和相关企业等重要信息。如果有其他优选题解,还可以一同分享给他人。

由于本系列文章的内容随时可能发生更新变动,欢迎关注和收藏征服LeetCode系列文章目录一文以作备忘。

给出集合 [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!

解法 逆康托展开

要想解决本题,首先需要了解一个简单的结论:对于 n n n 个不同的元素(例如数 1 , 2 , ⋯   , n 1, 2, \cdots, n 1,2,,n ),它们可以组成的排列总数目为 n ! n! n!

对于给定的 n n n k k k ,不妨从左往右确定第 k k k 个排列中的每一个位置上的元素到底是什么

我们首先确定排列中的首个元素 a 1 a_1 a1 。根据上述的结论,我们可以知道:

  • 1 1 1 a 1 a_1 a1 的排列一共有 ( n − 1 ) ! (n-1)! (n1)! 个;
  • 2 2 2 a 1 a_1 a1 的排列一共有 ( n − 1 ) ! (n-1)! (n1)! 个;
  • ⋯ \cdots
  • n n n a 1 a_1 a1 的排列一共有 ( n − 1 ) ! (n-1)! (n1)! 个。

由于我们需要求出从小到大的第 k k k 个排列,因此:

  • 如果 k ≤ ( n − 1 ) ! k \leq (n-1)! k(n1)! ,我们就可以确定排列的首个元素为 1 1 1
  • 如果 ( n − 1 ) ! < k ≤ 2 ⋅ ( n − 1 ) ! (n-1)! < k \leq 2 \cdot (n-1)! (n1)!<k2(n1)! ,我们就可以确定排列的首个元素为 2 2 2
  • ⋯ \cdots
  • 如果 ( n − 1 ) ⋅ ( n − 1 ) ! < k ≤ n ⋅ ( n − 1 ) ! (n-1) \cdot (n-1)! < k \leq n \cdot (n-1)! (n1)(n1)!<kn(n1)! ,我们就可以确定排列的首个元素为 n n n

因此,第 k k k 个排列的首个元素就是:
a 1 = ⌊ k − 1 ( n − 1 ) ! ⌋ + 1 a_1 = \lfloor \frac{k-1}{(n-1)!} \rfloor + 1 a1=(n1)!k1+1
其中 ⌊ x ⌋ \lfloor x \rfloor x 表示将 x x x 向下取整。

当我们确定了 a 1 a_1 a1 后,如何使用相似的思路,确定下一个元素 a 2 a_2 a2 呢?实际上,我们考虑以 a 1 a_1 a1 为首个元素的所有排列:

  • [ 1 , n ] \ a 1 [1,n] \backslash a_1 [1,n]\a1 最小的元素为 a 2 a_2 a2 的排列一共有 ( n − 2 ) ! (n−2)! (n2)! 个;
  • [ 1 , n ] \ a 1 [1,n] \backslash a_1 [1,n]\a1 次小的元素为 a 2 a_2 a2 的排列一共有 ( n − 2 ) ! (n-2)! (n2)! 个;
  • ⋯ \cdots
  • [ 1 , n ] \ a 1 [1,n] \backslash a_1 [1,n]\a1 最大的元素为 a 2 a_2 a2 的排列一共有 ( n − 2 ) ! (n−2)! (n2)! 个;

其中 [ 1 , n ] \ a 1 [1,n] \backslash a_1 [1,n]\a1 表示包含 1 , 2 , ⋯ n 1, 2, \cdots n 1,2,n 中除去 a 1 a_1 a1 以外元素的集合。这些排列从编号 ( a 1 − 1 ) ⋅ ( n − 1 ) ! (a_1-1) \cdot (n-1)! (a11)(n1)! 开始,到 a 1 ⋅ ( n − 1 ) ! a_1 \cdot (n-1)! a1(n1)! 结束,总计 ( n − 1 ) ! (n-1)! (n1)! 个,因此 k k k 个排列实际上就对应着这其中的第
k ′ = ( k − 1 )   m o d   ( n − 1 ) ! + 1 k' = (k-1) \bmod (n-1)! + 1 k=(k1)mod(n1)!+1
个排列
。这样一来,我们就把原问题转化成了一个完全相同但规模减少 1 1 1 的子问题:求 [ 1 , n ] \ a 1 [1, n] \backslash a_1 [1,n]\a1 n − 1 n-1 n1 个元素组成的排列中,第 k ′ k' k 小的排列。

算法:

  1. 设第 k k k 个排列为 a 1 , a 2 , ⋯   , a n a_1, a_2, \cdots,a_n a1,a2,,an ,我们从左往右地确定每一个元素 a i a_i ai​ 。我们用数组 valid \textit{valid} valid 记录每一个元素是否被使用过。
  2. 我们从小到大枚举 i i i
    1. 我们已经使用过了 i − 1 i−1 i1 个元素,剩余 n − i + 1 n-i+1 ni+1 个元素未使用过,每一个元素作为 a i a_i ai 都对应着 ( n − i ) ! (n-i)! (ni)! 个排列,总计 ( n − i + 1 ) ! (n-i+1)! (ni+1)! 个排列;
    2. 因此在第 k k k 个排列中, a i a_i ai 即为剩余未使用过的元素中第 ⌊ k − 1 ( n − i ) ! ⌋ + 1 \lfloor \frac{k-1}{(n-i)!} \rfloor + 1 (ni)!k1+1 小的元素;
    3. 在确定了 a i a_i ai 后,这 n − i + 1 n-i+1 ni+1 个元素的第 k k k 个排列,就等于 a i a_i ai 之后跟着剩余 n − i n-i ni 个元素的第 ( k − 1 )   m o d   ( n − i ) ! + 1 (k-1) \bmod (n-i)! + 1 (k1)mod(ni)!+1 个排列。

在实际的代码中,我们可以首先将 k k k 的值减少 1 1 1,这样可以减少运算,降低代码出错的概率。对应上述的后两步,即为:

  • 因此在第 k k k 个排列中, a i a_i ai​ 即为剩余未使用过的元素中第 ⌊ k ( n − i ) ! ⌋ + 1 \lfloor \frac{k}{(n-i)!} \rfloor + 1 (ni)!k+1 小的元素;
  • 在确定了 a i a_i ai 后,这 n − i + 1 n-i+1 ni+1 个元素的第 k k k 个排列,就等于 a i a_i ai 之后跟着剩余 n − i n-i ni 个元素的第 k   m o d   ( n − i ) ! k \bmod (n-i)! kmod(ni)! 个排列。

实际上,这相当于我们将所有的排列从 0 0 0 开始进行编号。

class Solution {
public:
    string getPermutation(int n, int k) {
        --k;
        int factory[10] = {1};
        for (int i = 1; i < n; ++i) factory[i] = factory[i - 1] * i;
        bool vis[10] = {false};

        string ans;
        for (int i = n - 1; i >= 0; --i) {
            int num = k / factory[i];
            k = k % factory[i];
            for (int j = 1; j <= n; ++j) {
                if (!vis[j]) {
                    if (num-- == 0) {
                        ans += '0' + j;
                        vis[j] = true;
                        break;
                    }
                }
            }
        }
        return ans;
    }
};

复杂度分析:

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度: O ( n ) O(n) O(n)

http://lihuaxi.xjx100.cn/news/1829956.html

相关文章

每日一题 2824. 统计和小于目标的下标对数目(简单)

简单题&#xff0c;走流程 class Solution:def countPairs(self, nums: List[int], target: int) -> int:ans 0for i in range(len(nums)):for j in range(i 1, len(nums)):if nums[i] nums[j] < target:ans 1return ans

ElasticSearch之cat component templates API

命令样例如下&#xff1a; curl -X GET "https://localhost:9200/_cat/component_templates?vtrue&pretty" --cacert $ES_HOME/config/certs/http_ca.crt -u "elastic:ohCxPHQBEs5*lo7F9"执行结果输出如下&#xff1a; name …

电源控制系统架构(PCSA)之系统控制处理器组件

目录 6.4 系统控制处理器 6.4.1 SCP组件 SCP处理器Core SCP处理器Core选择 SCP处理器核内存 系统计数器和通用计时器 看门狗 电压调节器控制 时钟控制 系统控制 信息接口 电源策略单元 传感器控制 外设访问 系统访问 6.4 系统控制处理器 系统控制处理器(SCP)是…

WPF面试题入门篇

入门篇[2] 1. 谈谈什么是WPF&#xff1f; WPF&#xff08;Windows Presentation Foundation&#xff09;是微软公司开发的一种用于创建Windows应用程序的用户界面框架。它是.NET Framework的一部分&#xff0c;提供了一种基于XAML&#xff08;可扩展应用程序标记语言&#xf…

Postgresql WAL日志解析挖掘(walminer 4.0)

1.下载walminer https://gitee.com/movead/XLogMiner/releases 2.安装walminer ## 解压缩 [rootpg soft]# su - postgres [postgrespg soft]$ tar -zxvf walminer_x86_64_v4.4.2.tar.gz## 创建 walminer 运行目录 [postgrespg soft]# mkdir -p /usr/local/walminer [postgre…

Shell循环:for(二)

一、通过用户列表文件创建用户 需求&#xff1a;通过用户列表文件创建用户 [rootlocalhost ~]# cat user.txt qian yoa huang演示&#xff1a; [rootlocalhost ~]# vim foruser.sh #编写脚本 #!/bin/bash for i in cat user.txt do useradd $i if [ $? -eq 0 ] thenech…

springboot(ssm超市货品信息管理系统 超市购物系统Java(codeLW)

springboot(ssm超市货品信息管理系统 超市购物系统Java(code&LW) 开发语言&#xff1a;Java 框架&#xff1a;ssm/springboot vue JDK版本&#xff1a;JDK1.8&#xff08;或11&#xff09; 服务器&#xff1a;tomcat 数据库&#xff1a;mysql 5.7&#xff08;或8.0&am…

【ARM CoreLink 系列 3.1 -- CCI-500 详细介绍 -上半部】

文章目录 1.1 CCI-500 介绍1.2 CCI-500 features 详细介绍1.2.1 Data Coherency between ACE Masters1.2.2 Quality of Service (QoS)1.2.3 (I/O) Coherency1.2.4 Crossbar Interconnect Functionality1.2.5 Performance Monitoring Unit (PMU)1.2.6 DVM Message Transport1.2.…