首页/新闻资讯/正文详情

《程序员数学:排列》有重复与无重复排列的 Java 递归实现与复杂度解析

发布时间:2026/9/23 20:42:46 来源:云帆数科 栏目:资讯中心
《程序员数学:排列》有重复与无重复排列的 Java 递归实现与复杂度解析
《程序员数学排列》有重复与无重复排列的 Java 递归实现与复杂度解析【免费下载链接】CodeGuide:books: 本代码库是作者小傅哥多年从事一线互联网 Java 开发的学习历程技术汇总旨在为大家提供一个清晰详细的学习教程侧重点更倾向编写Java核心内容。如果本仓库能为您提供帮助请给予支持(关注、点赞、分享)项目地址: https://gitcode.com/gh_mirrors/code/CodeGuide排列Permutation是高中阶段最常见的组合数学问题之一给定n个元素在“可重复使用”与“不可重复使用”两种约束下分别能组成多少种、以及如何枚举出全部排列结果。本文以 CodeGuide 仓库中 排列算法文档 为核心骨架完整讲解n!与n^r两条计数公式背后的 Java 递归实现并结合仓库内阶乘、组合、笛卡尔积等同系列算法文档进行纵向对照帮助你既会算、也能写、更能分析复杂度。一、前言从“高中排列题”到“程序员算法”有A、B、C三个字母允许重复使用字母与不允许重复使用字母分别有多少种组合方式这是高中阶段非常常见的数学问题答案可以用公式直接算出不可重复组合数n * (n-1) * (n - 2) * ... * 1 n!可重复组合数n * n * n ...共 r 次 n^r例如{1, 2, 3}三个元素无重复排列全排列数量为3! 6有重复排列长度为 2数量为3^2 9。这类计算本身并不难但作为程序员我们常常需要把这样的数学问题用代码逻辑真实地枚举出来——即不仅算出“有多少种”还要把每一种排列结果都构造出来。同时还需要考虑一个核心问题时间复杂度。本文接下来就以 CodeGuide 仓库中 排列算法文档 给出的两份 Java 实现为主线逐行拆解其递归过程并验证运行结果。二、数学基础排列计数公式与阶乘的关系排列问题的本质是“从n个不同元素中按顺序选取r个元素”约束计数公式含义无重复排列n! / (n - r)!全排列时为n!每个元素最多使用一次顺序有意义有重复排列n^r每个位置都有n种选择元素可重复使用其中n!阶乘是排列计算的基石其递推关系为n! n · (n-1)!关于阶乘的定义、递归实现与测试可参考仓库中的 《程序员数学阶乘》。理解这两个公式后就可以进入代码实现环节。需要特别说明的是方法名才是语义的权威——permutationWithRepetitions对应“有重复排列”permutationWithoutRepetitions对应“无重复排列”这一点在原文档的两个小节标题命名上存在倒置我们以下文的代码与测试输出为准展开。三、有重复排列permutationWithRepetitions1. 完整实现public static ListListInteger permutationWithRepetitions(int[] permutationOptions, int permutationLength) { if (permutationLength 1) { ListListInteger result new ArrayList(); for (int permutationOption : permutationOptions) { ListInteger item new ArrayList(); item.add(permutationOption); result.add(item); } return result; } ListListInteger permutations new ArrayList(); ListListInteger smallerPermutations permutationWithRepetitions(permutationOptions, permutationLength - 1); for (int currentOption : permutationOptions) { for (ListInteger smallerPermutation : smallerPermutations) { ListInteger permutation new ArrayList(); permutation.add(currentOption); permutation.addAll(smallerPermutation); permutations.add(permutation); } } return permutations; }2. 参数与递归逻辑拆解permutationOptions可供选择的元素数组permutationLength目标排列的长度即公式中的r例如从{1, 2, 3}中取长度为 2 的排列。算法采用自顶向下的递归策略核心分三步递归出口base case当permutationLength 1时把permutationOptions中的每个元素分别包装成单元素列表返回即r 1时共有n个排列递归降维先递归调用permutationWithRepetitions(permutationOptions, permutationLength - 1)求出所有长度为r-1的“小排列”前插合并外层遍历permutationOptions的每一个元素currentOption把它前插到每一个小排列的最前面从而生成长度为r的完整排列。由于每次递归都会把全部n个元素与所有r-1长度的小排列做一次笛卡尔式拼接最终生成的结果数量恰为n^r与公式完全吻合。3. 递归过程示例{1, 2, 3}长度 2r 1返回[1]、[2]、[3]r 2依次取currentOption 1/2/3分别前插到[1]/[2]/[3]之前得到[1,1] [1,2] [1,3] [2,1] [2,2] [2,3] [3,1] [3,2] [3,3]共3^2 9个。值得注意的是这里每一层都会对smallerPermutations做全量重建ArrayList.addAll存在元素拷贝开销这部分成本我们在后文“复杂度分析”一节统一量化。四、无重复排列permutationWithoutRepetitions1. 完整实现public static ListListInteger permutationWithoutRepetitions(int[] permutationOptions) { if (permutationOptions.length 1) { ListListInteger result new ArrayList(); result.add(List.of(permutationOptions[0])); return result; } ListListInteger permutations new ArrayList(); int[] smallerOptions new int[permutationOptions.length - 1]; System.arraycopy(permutationOptions, 1, smallerOptions, 0, smallerOptions.length); ListListInteger smallerPermutations permutationWithoutRepetitions(smallerOptions); int firstOption permutationOptions[0]; for (ListInteger smallerPermutation : smallerPermutations) { for (int positionIndex 0; positionIndex smallerPermutation.size(); positionIndex) { ListInteger permutationPrefix new ArrayList(smallerPermutation.subList(0, positionIndex)); ListInteger permutationSuffix new ArrayList(smallerPermutation.subList(positionIndex, smallerPermutation.size())); ListInteger permutation new ArrayList(permutationPrefix); permutation.add(firstOption); permutation.addAll(permutationSuffix); permutations.add(permutation); } } return permutations; }2. 参数与递归逻辑拆解permutationOptions待全排列的元素数组无重复约束下排列长度固定为数组长度因此不需要permutationLength参数。算法的思路是经典的“固定首元素 插入法”递归出口当数组只剩 1 个元素时直接返回仅包含该元素的列表拆分首元素取出permutationOptions[0]剩余部分通过System.arraycopy拷贝为smallerOptions递归求解剩余部分对smallerOptions递归调用自身得到所有n-1个元素的全排列逐位置插入对每一个小排列依次把首元素插入到下标0 ~ size含末尾的每个可能位置即构造n种新排列。因为每个元素只会使用一次最终生成的结果数量恰为n!。3. 递归过程示例{1, 2, 3}对{3}递归返回[3]对{2, 3}首元素2插入[3]的 0、1 两个位置得到[2,3]、[3,2]对{1, 2, 3}首元素1分别插入[2,3]的 0、1、2 位置和[3,2]的 0、1、2 位置得到 6 个全排列[1,2,3] [2,1,3] [2,3,1] [1,3,2] [3,1,2] [3,2,1]。这里通过subList加两次拷贝的方式完成“在指定位置插入元素”实现上避免了手写循环移动数组逻辑也更贴近“插入”的语义。五、测试验证与运行结果原文档给出了两个对应的 JUnit 测试用例均在{1, 2, 3}上运行Test public void test_permutationWithRepetitions() { int[] permutationOptions {1, 2, 3}; ListListInteger permutation Permutations.permutationWithRepetitions(permutationOptions, 2); for (ListInteger list : permutation) { System.out.println(JSON.toJSONString(list)); } } Test public void test_permutationWithoutRepetitions() { int[] permutationOptions {1, 2, 3}; ListListInteger permutation Permutations.permutationWithoutRepetitions(permutationOptions); for (ListInteger list : permutation) { System.out.println(JSON.toJSONString(list)); } }有重复排列测试结果n 3, r 2共 9 个[1,1] [1,2] [1,3] [2,1] [2,2] [2,3] [3,1] [3,2] [3,3] Process finished with exit code 0输出恰好包含[1,1]、[2,2]、[3,3]这类重复使用元素的组合验证了“可重复”语义且数量9 3^2与公式一致。对于无重复测试根据第四节推导的递归过程{1, 2, 3}的输出应为 6 个全排列n! 6这与高中数学中的全排列结论相互印证仓库中该系列算法的完整工程代码位于作者开源的java-algorithms项目Permutations类感兴趣的读者可以结合 组合算法文档 中的Combinations类对比阅读。六、与组合、笛卡尔积、幂集的关联区分排列并非孤立的算法它是 CodeGuide 仓库algorithm/logic/sets系列“集合运算算法家族”的一员。下表对几个极易混淆的概念做一次集中辨析算法是否讲究顺序元素是否可重复结果数量仓库文档排列有重复讲究可重复n^r本文排列无重复讲究不可重复n!本文组合有/无重复不讲究视场景C(nr-1, r)/C(n, r)组合算法笛卡尔积讲究有序对跨集合组合|A| × |B|笛卡尔积幂集不讲究不可重复2^n幂集洗牌随机排列讲究不可重复n!中的随机一个Fisher-Yates 洗牌关键区分点在于排列 vs 组合排列中(A, B)与(B, A)是两种结果顺序有意义组合中二者等价。双色球选号属于组合而“三人排队站法”属于排列。组合的实现通过subList从i开始取剩余元素来天然避免顺序重复与排列的“逐位置插入”形成鲜明对比排列 vs 笛卡尔积有重复排列本质上是“同一个集合与自身的 r 次笛卡尔积”的枚举扑克牌13 × 4 52则是两个不同集合笛卡尔积的经典案例详见 笛卡尔积文档排列 vs 幂集幂集枚举的是“所有子集”2^n不关心元素顺序可视为比排列更低维度的问题详见 幂集文档。理解了这张“家族图谱”遇到具体业务问题时就能快速定位该用哪种算法。七、复杂度分析与工程实践建议1. 时间复杂度从源码结构看两份实现均为“先生成全部结果、一次性返回”的递归枚举有重复排列结果总量为n^r每构造一个长度为r的结果都需要O(r)的addAll拷贝因此总时间复杂度为O(r · n^r)无重复排列结果总量为n!每个结果的长度为n构造时同样伴随O(n)级拷贝因此总时间复杂度为O(n · n!)空间复杂度两者都因“全量收集到 List 后返回”而需要O(n^r)/O(n!)级的存储空间外加递归栈深度O(r)/O(n)。这也是排列类算法最需要警惕的一点结果数量是指数级乃至阶乘级爆炸的。例如n 10时无重复排列已达3,628,800个n 12时超过4.7 亿个内存很快就会被耗尽。2. 工程实践建议小规模枚举当n ≤ 8左右时本文的全量返回实现简单直接、易于测试适合在单元测试中生成全部排列用例大规模处理若n较大应改为“生成一个、消费一个”的迭代器/回调模式避免一次性持有全部结果递归写法也建议改为基于数组原地交换swap的经典回溯写法把空间开销降为O(n)典型应用场景多维度组合的测试数据生成、密码字典的全排列枚举、商品规格 SKU 的组合爆炸排查、以及线上试卷题目与选项乱序后者可直接使用 Fisher-Yates 洗牌算法仅需从n!种排列中随机取一个而无需全部枚举。八、小结排列算法看似只是两条高中数学公式的代码化但其背后包含了递归降维、首元素插入、结果全量枚举与复杂度爆炸等多个值得反复咀嚼的程序员思维点。本文完整覆盖了 原文档 中的两套 Java 实现、参数说明、测试用例与输出结果并补充了与阶乘、组合、笛卡尔积、幂集等仓库同系列算法的对照关系以及时间/空间复杂度的定量分析。掌握它你就掌握了“从数学公式到可运行代码”的完整闭环也为后续学习回溯算法、状态空间搜索等更复杂的枚举类问题打下了基础。【免费下载链接】CodeGuide:books: 本代码库是作者小傅哥多年从事一线互联网 Java 开发的学习历程技术汇总旨在为大家提供一个清晰详细的学习教程侧重点更倾向编写Java核心内容。如果本仓库能为您提供帮助请给予支持(关注、点赞、分享)项目地址: https://gitcode.com/gh_mirrors/code/CodeGuide创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

loop-engineering 发布后激活清单(Activation Checklist):第 1–4 周上线执行手册
loop-engineering 发布后激活清单(Activation Checklist):第 1–4 周上线执行手册

人工智能AI AgentAgent 工作流CLI研发协作AI 技能MCP 服务 【免费下载链接】loop-engineering Practical patterns, starters & CLI tools for loop engineering with AI coding agents. Design systems that prompt and orchestrate agents (inspired by Addy Osmani and … · 2026/9/23 20:42:39

空投箱实战:3步搞定资源投放的保姆级教程
空投箱实战:3步搞定资源投放的保姆级教程

空投箱实战:3步搞定资源投放的保姆级教程 官方文档往往长篇大论,让人抓不住重点,新手极易在配置参数时迷失方向。这份空投箱实战指南摒弃冗余理论,直接切入核心配置流程。我们将通过一个最小可运行示例,彻底搞懂资源动态加载的底层逻辑。… · 2026/9/23 20:42:39

泛微e-cology 8 Webservice接口对接实战:从WSDL到流程创建
泛微e-cology 8 Webservice接口对接实战:从WSDL到流程创建

简介:泛微OA e-cology 8 最新webservice接口文档,面向需要对接泛微OA系统的开发人员,解决通过Webservice方式操作文档管理的需求。资源为1个docx文件,大小330KB,内容涵盖接口部署说明、方法定义与参数返回示例&#xf… · 2026/9/23 20:42:39

PaddleHub Module API 权威指南:预训练模型加载、运行与推理导出的统一入口
PaddleHub Module API 权威指南:预训练模型加载、运行与推理导出的统一入口

PaddleHub Module API 权威指南:预训练模型加载、运行与推理导出的统一入口 【免费下载链接】PaddleFormers PaddleFormers is an easy-to-use library of pre-trained large language model zoo based on PaddlePaddle. 项目地址: https://gitcode.com/gh_mirror… · 2026/9/23 21:19:32

零配置在线工具站设计:纯前端架构与打开即用体验
零配置在线工具站设计:纯前端架构与打开即用体验

1. 一个标题引发的思考:从「卧槽」到产品设计逻辑第一次看到「你只管打开这个网站,剩下的交给卧槽」这个标题,我脑子里蹦出来的第一个念头是:这大概率又是一个靠情绪冲击力做传播的工具型站点。做了十多年产品拆解和流量分析&… · 2026/9/23 21:19:19

Scapy SCTP 协议层实战指南:报文构造、Chunk 解析与 Linux 内核扩展能力启用
Scapy SCTP 协议层实战指南:报文构造、Chunk 解析与 Linux 内核扩展能力启用

网络网络安全 【免费下载链接】scapy Scapy: the Python-based interactive packet manipulation program & library. 项目地址: https://gitcode.com/gh_mirrors/sc/scapy 点击查看 免费下载 SCTP(Stream Control Transmission Protocol&#xff0… · 2026/9/23 21:19:13

Excel换行全解析:Alt+Enter、CHAR(10)与自动换行原理
Excel换行全解析:Alt+Enter、CHAR(10)与自动换行原理

1. 项目概述:Excel换行不是“按回车”那么简单“Excel怎么换行?”——这问题我每天至少被问三遍,从刚入职的实习生到做了十年财务的老会计,再到自己开网店的小老板,人人都卡在这一步。表面看只是想让单元格里文字多行显… · 2026/9/23 21:19:13

智能降重系统Paperxie架构解析与论文降重实战策略
智能降重系统Paperxie架构解析与论文降重实战策略

1. 论文降重行业现状与核心痛点论文查重系统已经成为学术界的标配工具,知网、维普、万方等主流检测平台的技术迭代让降重工作变得越来越具有挑战性。根据我多年在学术服务领域的观察,目前90%以上的高校采用知网查重系统,其特有的"跨语言… · 2026/9/23 21:18:41

从PASCAL VOC到YOLOv8:991张吸烟检测数据集的完整训练实践
从PASCAL VOC到YOLOv8:991张吸烟检测数据集的完整训练实践

简介:面向计算机视觉与目标检测学习者,这是一份包含991张真实吸烟场景图像的标注数据集,可用于吸烟检测、行为识别等模型的训练与算法验证。压缩包内共1982个文件,包含991张JPG原图和991个对应的Pascal VOC XML标注文件&#xff0… · 2026/9/23 21:18:35

3招搞定手机怎么下载微信面试难题实战项目解析
3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A… · 2026/9/23 0:00:03

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧
Win7无线热点配置工具源码解析:解决API失效的3个实战技巧

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧 Win7无线热点配置工具在Win10/11上跑不动?不是你的问题,是版本升级后 API 全变了。很多老项目里的 netsh wlan… · 2026/9/23 0:00:36

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码