资讯动态

doocs/leetcode 题解:LCP 77. 符文储备——排序 + 滑动窗口求相邻魔力值差不超过 1 的最长可携带符文序列

发布时间:2026/10/16 7:24:07 来源:尧图企业网站定制
示例工程【免费下载链接】leetcodeLeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer第 2 版》、《程序员面试金典第 6 版》题解项目地址https://gitcode.com/doocs/leetcode点击查看免费下载导读本文围绕 LeetCode LCP 系列题「LCP 77. 符文储备」展开完整讲解题目的约束条件、两个官方示例并深入剖析其核心解法——先将符文魔力值排序再用双指针维护滑动窗口找出「相邻魔力值相差不超过 1」的最长连续段。读完本文你将掌握该类「差值约束 最大化子集」问题的通用建模思路并能对照 doocs/leetcode 仓库中 Python、Java、C、Go、TypeScript 五种语言的 官方题解与源码 独立编写和验证代码。一、题目描述远征队在出发前需要携带一些「符文」作为后续的冒险储备。runes[i]表示第i枚符文的魔力值。他们将从中选取若干符文进行携带并对这些符文进行重新排列以确保任意相邻的两块符文之间的魔力值相差不超过1。请返回他们能够携带的符文最大数量。输入runes [1,3,5,4,1,7] 输出3本题来自 LeetCode LCP力扣杯 / 程序员面试系列在仓库中的编号为「LCP 77. 符文储备」难度为简单是一道典型的「排序 滑动窗口 / 双指针」应用题。二、示例与数据范围示例 1输入runes [1,3,5,4,1,7]输出3解释最佳的选择方案为[3,5,4]将其排列为[3,4,5]后任意相邻的两块符文魔力值均不超过1携带数量为3。其他满足条件的方案为[1,1]和[7]数量均小于3因此返回可携带的最大数量3。示例 2输入runes [1,1,3,3,2,4]输出6解释排列为[1,1,2,3,3,4]可携带所有的符文。提示1 runes.length 10^40 runes[i] 10^4从数据范围可以判断n最大为10^4魔力值是非负整数且值域上限同样为10^4。这决定了我们既可以选择O(n log n)的排序类算法也保留了值域受限时「桶计数」优化的想象空间见后文第七节。三、核心思路把「可重新排列」转化为排序后的连续段题目的难点在于约束条件是「重新排列后任意相邻两块差值不超过 1」这似乎与排列顺序有关。但仔细观察可以发现一个关键事实对于任意一组选取的符文把它们按魔力值从小到大排序恰好是让相邻差值最小的排列方式。因此一个选取方案合法当且仅当它排序后任意相邻两项的差都不超过 1。原文档的「思考」部分也给出了同样的分析n 10^4要求选出一个子集任意两块魔力差的绝对值不超过1并最大化子集大小差值条件与顺序无关排序后合法元素必落在一段连续区间排序后用左端i维护当前段相邻差大于1则重开窗口否则用j - i 1更新答案。进一步推导假设排序后的数组中存在三个元素a b c如果c - a 1那么必然有b - a 1且c - b 1即中间的元素一定也能被纳入同一个合法集合。这意味着任何一个合法的携带方案都对应于排序后数组中的一段连续区间反过来排序后任意一段「相邻差均不超过 1」的连续区间其全部元素构成的集合都是合法方案。于是问题被干净地转化为在排序后的runes数组中寻找「相邻元素之差均不超过 1」的最长连续子数组的长度。四、方法一排序 滑动窗口双指针4.1 算法流程将runes按魔力值从小到大排序初始化窗口左端i 0答案ans 0用右端j从左到右遍历排序后的数组若j 0且runes[j] - runes[j-1] 1说明当前元素与前一个元素「断档」合法段被切断令i j重开窗口否则窗口[i, j]内的相邻差均不超过 1用窗口长度j - i 1更新ans遍历结束后ans即为可携带的最大符文数量。正确性说明由于合法集合在排序后必然是连续段而相邻差超过 1 的「断点」一定是两个合法段的天然分界因此每次断档时重开窗口就能枚举出所有可能的合法段取其中长度最大者即为答案。4.2 复杂度分析按原文档给出的结论时间复杂度O(n × log n)主要来自排序空间复杂度O(log n)来自排序过程如快速排序的递归栈开销。其中n是数组runes的长度。排序之后的双指针扫描是单趟O(n)不是性能瓶颈。五、多语言实现与逐行解读该题在仓库目录 lcp/LCP 77. 符文储备 下提供了五种语言的独立实现文件Solution.py、Solution.java、Solution.cpp、Solution.go、Solution.ts与 README 中的题解代码一一对应。下面逐一解读。5.1 Python3参考源码Solution.pyclass Solution: def runeReserve(self, runes: List[int]) - int: runes.sort() ans i 0 for j, x in enumerate(runes): if j and runes[j] - runes[j - 1] 1: i j else: ans max(ans, j - i 1) return ans要点说明runes.sort()原地升序排序i为窗口左端ans为答案enumerate同时给出下标j与元素值xx即runes[j]用于与runes[j-1]比较if j and ...利用了 Python 中0为假值的特性当j 0第一个元素时条件短路为假进入else分支将ans置为1——这保证了对单元素窗口的处理当相邻差大于 1 时i j重开窗口否则用j - i 1更新最大长度。5.2 Java参考源码Solution.javaclass Solution { public int runeReserve(int[] runes) { Arrays.sort(runes); int ans 0; for (int i 0, j 0; j runes.length; j) { if (j 0 runes[j] - runes[j - 1] 1) { i j; } else { ans Math.max(ans, j - i 1); } } return ans; } }要点说明Arrays.sort(runes)对int[]原地排序双下标i窗口左端与j扫描指针在同一循环内声明j即窗口右端与 Python 版本逻辑完全一致j 0时若runes[j] - runes[j-1] 1则i j重开窗口否则Math.max更新答案。5.3 C参考源码Solution.cppclass Solution { public: int runeReserve(vectorint runes) { sort(runes.begin(), runes.end()); int ans 0; for (int i 0, j 0; j runes.size(); j) { if (j runes[j] - runes[j - 1] 1) { i j; } else { ans max(ans, j - i 1); } } return ans; } };要点说明sort(runes.begin(), runes.end())对vectorint升序排序if (j ...)与 Python 写法同源j 0时短路跳过断档判断进入else分支初始化答案注意j是size_t类型时j - 1需保证j 0才参与运算该写法通过短路求值规避了无符号下溢问题。5.4 Go参考源码Solution.gofunc runeReserve(runes []int) (ans int) { sort.Ints(runes) i : 0 for j, x : range runes { if j 0 x-runes[j-1] 1 { i j } else if t : j - i 1; ans t { ans t } } return }要点说明sort.Ints(runes)原地排序函数使用具名返回值(ans int)因此末尾直接return即可返回ansrange runes同时给出下标j与元素值x断档判断直接比较x与runes[j-1]更新答案时使用 Go 特色的else if短语句在条件内声明局部变量t : j - i 1当ans t时赋值写法紧凑且作用域清晰。5.5 TypeScript参考源码Solution.tsfunction runeReserve(runes: number[]): number { runes.sort((a, b) a - b); let ans 0; let i 0; for (let j 0; j runes.length; j) { if (j 0 runes[j] - runes[j - 1] 1) { i j; } else { ans Math.max(ans, j - i 1); } } return ans; }要点说明runes.sort((a, b) a - b)必须显式传入比较器JS/TS 默认的Array.prototype.sort按字符串字典序排序直接调用sort()会把10排在2前面导致数值排序错误这是本题最容易踩的坑其余逻辑双指针、断档重开窗口、Math.max更新与其他语言一致。六、边界情况与易错点结合题目约束与源码实现以下几个边界值得特别注意场景行为说明runes只有一个元素返回1首轮j 0走else分支ans被置为1与「任意相邻」约束空集无关单独一块符文必可携带所有魔力值相同如[5,5,5]返回n相邻差为0 1全程不断档窗口覆盖整个数组存在重复值如[1,1,2,2]差值0合法重复值之间的差为 0不触发断档可全部纳入同一窗口魔力值为0合法0 runes[i] 10^40与1的差为1可正常参与窗口计算相邻差恰好为1不断档断档条件严格为 1差等于1时窗口继续扩展相邻差大于1时重开窗口断点处i j当前元素单独成段其长度由后续轮次的else分支计入最易出错的点TypeScript/JavaScript 中忘记传(a, b) a - b比较器导致数值排序变成字典序排序以及判断断档时误用「窗口内最大值与最小值之差」而非「相邻元素之差」——本题约束的是相邻两块差值不超过 1而非整个窗口跨度两者含义完全不同。七、思路延伸值域受限下的桶计数等价实现题目提示中0 runes[i] 10^4即魔力值是非负整数且值域有限。从第三节的结论可以推导出一个等价的视角由于魔力值是整数且排序后相邻差不超过 1合法集合的不同魔力值必然构成一段连续的整数区间区间内每个值可出现任意多次。由此可以得到一种不排序的替代实现思路先统计每个魔力值v的出现频次cnt[v]然后扫描值域找出「频次大于 0 的连续整数区间」中最长的一段区间携带数量即为该区间内所有频次之和。其复杂度为O(n M)M为值域上限10^4在n接近10^4时与排序方案相当但当n远大于值域时例如大量重复数据桶计数可以省去排序的O(n log n)开销。需要说明的是该变体是基于题目约束自行推导的等价思路仓库官方题解采用的是前述「排序 滑动窗口」方案两种方法结论一致可以互为验证。八、仓库资源速览本主题在仓库中的完整资源如下便于对照学习与验证题解文档题目描述 解法 全语言代码Solution.pyPython3 实现Solution.javaJava 实现Solution.cppC 实现Solution.goGo 实现Solution.tsTypeScript 实现LCP 系列题解索引可按题号快速定位全部 LCP 题解总结「LCP 77. 符文储备」表面上是带排列约束的选取问题核心洞察在于两点一是排序是让相邻差最小的最优排列使「任意相邻差 ≤ 1」与顺序解耦二是合法集合必然对应排序数组中的连续段从而把问题归约为「找相邻差均 ≤ 1 的最长连续子数组」。基于此排序 双指针滑动窗口即可在O(n log n)时间内求解五种语言的实现结构完全一致唯一需要注意的差异点是 JS/TS 的排序比较器。该题的「先排序消解顺序约束、再双指针扫连续段」套路同样适用于其他「差值 / 间隔约束 最大化子集」类问题值得熟练掌握。赞分享示例工程【免费下载链接】leetcodeLeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer第 2 版》、《程序员面试金典第 6 版》题解项目地址https://gitcode.com/doocs/leetcode点击查看免费下载相关推荐LCP 77「符文储备」多语言题解精讲排序 双指针滑动窗口求最大携带符文数LCP 77「符文储备」多语言题解精讲排序 双指针滑动窗口求最大携带符文数 本篇基于 doocs/leetcode 开源仓库中 lcp/LCP 77. 符示例工程Odoo AI 助手配置指南5 分钟搭好你的 7×24 智能客服Odoo AI 助手配置指南5 分钟搭好你的 7×24 智能客服 客户半夜问价没人回同一个问题被问了一百遍用 Odoo 就能搭出一个会自己回复的 Odoo企业应用后端电商codeforces-go 力扣杯 2023 春·战队赛题解runeReserve 排序后相邻扫描求最长符文段codeforces go 力扣杯 2023 春·战队赛题解runeReserve 排序后相邻扫描求最长符文段 本篇题解对应 leetcode/season/科学计算上一篇ezdxf 解析 DXF 代理图形Proxy Graphic二进制块ACGi 指令流与 Opcode 全解读下一篇使用 DataHub matillion-dpc Connector 摄取 Matillion Data Productivity Cloud 元数据与血缘创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑