LeetCode 60:排列序列

LeetCode 60:排列序列

在这里插入图片描述

问题定义与核心挑战

给定整数 nk,返回集合 {1,2,...,n}k 个字典序排列。直接生成所有排列再遍历到第 k 个的方法(时间复杂度 O(n!))会因 n≥10 时阶乘爆炸而超时,因此需要 数学推导 + 贪心构造 的高效解法。

核心思路:阶乘定位法

利用阶乘的分组特性,逐位确定排列的每个数字:

  1. 阶乘分组:对于 n 个数字,每个首位固定后,剩余 n-1 个数字的排列数为 (n-1)!。例如,n=3 时,首位为 1 的排列有 2! = 2 种(123132)。
  2. 逐位推导:通过计算 k-1(转换为 0-based 索引)与阶乘的商和余数,确定当前位的数字,并更新 k 为余数+1(保持后续推导的 1-based 逻辑)。

算法步骤详解

步骤 1:预处理阶乘数组

计算 0!(n-1)!,用于快速确定每组的排列数量:

// 阶乘数组,fact[i] = i!
long[] fact = new long[n];
fact[0] = 1; // 0! = 1
for (int i = 1; i < n; i++) {fact[i] = fact[i-1] * i;
}
  • 例如,n=4 时,fact = [1, 1, 2, 6](对应 0!1!2!3!)。
步骤 2:初始化可用数字列表

维护一个动态列表,存储当前未使用的数字(初始为 1~n):

List<Integer> nums = new ArrayList<>();
for (int i = 1; i <= n; i++) {nums.add(i);
}
步骤 3:逐位构造排列

从高位到低位(共 n 位),依次确定每个位置的数字:

StringBuilder res = new StringBuilder();
k--; // 转换为 0-based 索引(核心!否则无法对齐阶乘分组)for (int i = 0; i < n; i++) {// 当前剩余数字的数量:n - i// 当前阶乘:(n-1-i)! → 对应剩余数字的排列数long currentFact = fact[n-1 - i];// 计算当前位的数字索引:k / currentFactint index = (int) (k / currentFact);// 取出数字,加入结果res.append(nums.get(index));// 从可用列表中移除该数字nums.remove(index);// 更新 k 为余数(下一轮的 k 是余数)k %= currentFact;
}

关键逻辑解析

  1. k-- 的必要性
    题目中 k1-based(如示例 1 中 k=3 对应第 3 个排列),但阶乘分组的索引是 0-based。通过 k-- 转换为 0-based 索引,确保计算时与阶乘分组对齐。

  2. 阶乘的作用
    currentFact = (n-1-i)! 表示剩余 n-i 个数字时,每个首位对应的排列数。例如,当确定第 1 位(i=0)时,currentFact = (n-1)!,即每个首位对应 (n-1)! 种排列。

  3. 索引计算与数字移除

    • index = k / currentFact:确定当前位应选可用列表中的第 index 个数字(因为前 index 组的排列数已被跳过)。
    • nums.remove(index):从可用列表中移除已选数字,避免重复使用。
    • k %= currentFact:更新 k 为当前组内的剩余索引,用于下一轮计算。

完整代码(Java)

import java.util.ArrayList;
import java.util.List;class Solution {public String getPermutation(int n, int k) {// 1. 预处理阶乘数组:fact[i] = i!long[] fact = new long[n];fact[0] = 1; // 0! = 1for (int i = 1; i < n; i++) {fact[i] = fact[i-1] * i;}// 2. 初始化可用数字列表:[1, 2, ..., n]List<Integer> nums = new ArrayList<>();for (int i = 1; i <= n; i++) {nums.add(i);}// 3. 逐位构造结果StringBuilder res = new StringBuilder();k--; // 转换为 0-based 索引,方便计算for (int i = 0; i < n; i++) {// 当前剩余数字的排列数:(n-1-i)!long currentFact = fact[n-1 - i];// 计算当前位的数字索引int index = (int) (k / currentFact);// 取出数字,加入结果res.append(nums.get(index));// 移除已选数字nums.remove(index);// 更新 k 为余数k %= currentFact;}return res.toString();}
}

示例验证(以示例 2 为例)

输入n=4, k=9
预期输出"2314"

推导过程:
  1. 阶乘数组fact = [1, 1, 2, 6](对应 0!~3!)。
  2. 初始化nums = [1,2,3,4]k=9→k-1=8(转换为 0-based)。
步骤 i剩余数字currentFactindex = 8 / currentFact选中数字nums 变为k = 8 % currentFact
0[1,2,3,4]6 (3!)8 / 6 = 12[1,3,4]8 % 6 = 2
1[1,3,4]2 (2!)2 / 2 = 13[1,4]2 % 2 = 0
2[1,4]1 (1!)0 / 1 = 01[4]0 % 1 = 0
3[4]1 (0!)0 / 1 = 04[]0 % 1 = 0

最终结果:"2314",与示例一致。

复杂度分析

  • 时间复杂度O(n²)
    • 阶乘预处理:O(n)
    • 逐位构造:共 n 次循环,每次 nums.remove(index)O(n) 操作(数组拷贝)。
  • 空间复杂度O(n)
    • 阶乘数组和可用列表均占用 O(n) 空间。

该方法通过阶乘分组贪心构造,将时间复杂度从 O(n!) 降至 O(n²),高效解决了大 n 场景下的排列定位问题,是处理“有序排列定位”类问题的经典思路。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/916549.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/916549.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

亚远景-传统功能安全VS AI安全:ISO 8800填补的标准空白与实施难点

一、为什么需要ISO 8800&#xff1a;传统安全标准的“盲区”传统功能安全&#xff08;ISO 26262&#xff09;• 假设&#xff1a;系统行为可被完整规格化&#xff0c;失效模式可枚举&#xff0c;风险可用概率-危害矩阵量化。• 盲区&#xff1a;对“设计意图正确&#xff0c;但…

菜鸟教程 R语言基础运算 注释 和数据类型

菜鸟教程 R语言基础运算 注释 和数据类型 1.注释 注释主要用于一段代码的解析&#xff0c;可以让阅读者更易理解&#xff0c;编程语言的注释会被编译器忽略掉&#xff0c;且不会影响代码的执行。 一般编程语言的注释分为单行注释与多行注释&#xff0c;但是 R 语言只支持单行注…

华为云ELB(弹性负载均衡)持续报异常

华为云ELB&#xff08;弹性负载均衡&#xff09;持续报异常&#xff0c;需结合实例类型&#xff08;共享型/独享型&#xff09;和异常代码进行针对性排查。以下是分步排查建议&#xff1a;一、根据实例类型排查网络配置共享型实例 安全组规则&#xff1a;检查后端服务器安全组是…

《R for Data Science (2e)》免费中文翻译 (第2章) --- Workflow: basics

写在前面 本系列推文为《R for Data Science (2)》的中文翻译版本。所有内容都通过开源免费的方式上传至Github&#xff0c;欢迎大家参与贡献&#xff0c;详细信息见&#xff1a; Books-zh-cn 项目介绍&#xff1a; Books-zh-cn&#xff1a;开源免费的中文书籍社区 r4ds-zh-cn …

开源深度学习新宠:Burn框架助您无忧高效建模

在日新月异的人工智能世界里&#xff0c;各类深度学习框架如雨后春笋般涌现&#xff0c;而Burn&#xff0c;作为新一代的深度学习框架&#xff0c;以其不妥协的灵活性、高效性和可移植性崭露头角。本文将深入探讨Burn的核心功能、应用场景及具体使用方法&#xff0c;帮助您更好…

基于深度学习的图像分割:使用DeepLabv3实现高效分割

前言 图像分割是计算机视觉领域中的一个重要任务&#xff0c;其目标是将图像中的每个像素分配到不同的类别中。近年来&#xff0c;深度学习技术&#xff0c;尤其是卷积神经网络&#xff08;CNN&#xff09;&#xff0c;在图像分割任务中取得了显著的进展。DeepLabv3是一种高效的…

如何高效合并音视频文件(时间短消耗资源少)(二)

英语字幕 1 00:00:06,480 --> 00:00:08,400 Good morning. We have a banger for you2 00:00:08,400 --> 00:00:09,840 today. We&amp;#39;re going to launch chatbt3 00:00:09,840 --> 00:00:11,519 agent. But before jumping into that, I&amp;#39;d4 00…

内网后渗透攻击过程(实验环境)--4、权限维持(2)

用途限制声明&#xff0c;本文仅用于网络安全技术研究、教育与知识分享。文中涉及的渗透测试方法与工具&#xff0c;严禁用于未经授权的网络攻击、数据窃取或任何违法活动。任何因不当使用本文内容导致的法律后果&#xff0c;作者及发布平台不承担任何责任。渗透测试涉及复杂技…

CentOS 9 配置国内 YUM 源

1.备份 sudo mv /etc/yum.repos.d/centos.repo /etc/yum.repos.d/centos.repo.backup sudo mv /etc/yum.repos.d/centos-addons.repo /etc/yum.repos.d/centos-addons.repo.backup2.创建新文件 vi /etc/yum.repos.d/centos.repo[baseos] nameCentOS Stream $releasever - BaseO…

【算法】递归、搜索与回溯算法入门

文章目录递归什么是递归为什么会用到递归如何理解递归如何写好一个递归搜索 vs 深度优先遍历 vs 深度优先搜索 vs 宽度&#xff08;广度&#xff09;优先遍历 vs 宽度&#xff08;广度&#xff09;优先搜索 vs 暴搜深度优先遍历 vs 深度优先搜索&#xff08;dfs&#xff09;宽度…

借助Aspose.HTML控件,在 Python 中将 SVG 转换为 PDF

您可能会发现许多解决方案都提供以编程方式将SVG转换为PDF 的功能。但这篇博文将介绍一个功能强大的 SDK&#xff0c;供 Python 开发人员自动化文件转换和操作。本指南将重点介绍通过 .NET 实现 Python 的 Aspose.HTML。此外&#xff0c;我们将逐步讲解相关步骤和代码片段&…

高级06-Java网络编程:从Socket到HTTP

引言&#xff1a;Java 网络编程的重要性 随着互联网技术的飞速发展&#xff0c;网络编程已成为现代软件开发中不可或缺的一部分。Java 作为一种广泛应用于企业级开发和分布式系统的编程语言&#xff0c;提供了强大的网络通信支持。从底层的 Socket 编程到高层的 HTTP 协议处理&…

STM32的蓝牙通讯(HAL库)

蓝牙基础知识&#xff08;了解即可&#xff09;&#xff1a;1.是一种利用低功率无线电&#xff0c;支持设备短距离通信的无线电技术&#xff0c;能在包括移动电话、PDAQ、无线耳机、笔记本电脑、相关外设等众多设备之间进行无线信息交换&#xff0c;蓝牙工作在全球通用的2.4 GH…

方案B,version1

我们重新设计起步阶段的步骤,目标是:通过运行PowerShell脚本和配置GitHub Actions工作流(deploy.yml)来实现自动化部署。 要求: 用私有仓库(my-website-source-SSH)存储源码。 通过GitHub Actions自动构建(这里只是简单的Hello World,所以构建步骤可以简化为复制文件…

Linux --- 进程

一、进程概念 在 Linux 系统中&#xff0c;​​进程&#xff08;Process&#xff09;​​ 是程序执行的动态实例&#xff0c;是操作系统进行资源分配和调度的基本单位。 ​​1. 程序 vs 进程​​ ​​程序&#xff08;Program&#xff09;​​&#xff1a;是静态的代码集合&…

Cgroup 控制组学习(三)在容器中使用 CGroups

一、CGroups 关于mememory的限制操作 cgroup关于cpu操作 关于memeory cgroup的几个要点 ① memeory限额类 1、memory.limit_bytes&#xff1a;硬限制--> 限制最大内存使用量&#xff0c;单位有k、m、g三种&#xff0c;填-1则代表无限制,默认是字节2、memory.soft_limi…

SpringBoot面试基础知识

SpringBoot 是面试中后端开发岗位的高频考点&#xff0c;以下是核心考点整理&#xff1a;1. SpringBoot 基础概念- 定义&#xff1a;SpringBoot 是 Spring 框架的简化版&#xff0c;通过“自动配置”“起步依赖”等特性&#xff0c;简化 Spring 应用的搭建和开发&#xff0c;减…

Java面试全方位解析:从基础到AI的技术交锋

Java面试全方位解析&#xff1a;从基础到AI的技术交锋 面试场景&#xff1a;互联网大厂Java工程师岗位面试 面试官&#xff1a;您好&#xff0c;我是今天的面试官&#xff0c;接下来我们将进行三轮技术面试。 谢飞机&#xff1a;您好您好&#xff01;我是谢飞机&#xff0c;特别…

Web Worker:解锁浏览器多线程,提升前端性能与体验

目录 一、Web Worker 是什么&#xff1f; 核心特性 类型 二、为什么需要 Web Worker&#xff1f;(单线程的痛点) 三、Web Worker 的典型使用场景 四、一个简单的代码示例 (专用 Worker) 五、使用 Web Worker 的注意事项 六、总结 一、Web Worker 是什么&#xff1f; 简…

LabVIEW命令行调用与传参功能

该功能一方面借助 Formatinto String 构建命令行字符串&#xff0c;实现LabVIEW 环境下命令行调用 VI 并传参&#xff1b;另一方面&#xff0c;针对 Mac 平台&#xff0c;通过解析应用 Info.plist 文件&#xff0c;处理 LabVIEW 可执行文件路径&#xff0c;完善跨平台命令行调用…