LeetCode 228 汇总区间(Summary Ranges)题解:双指针一次扫描切分连续区间

📅 发布时间:2026/10/17 6:12:16
LeetCode 228 汇总区间(Summary Ranges)题解:双指针一次扫描切分连续区间
示例工程【免费下载链接】leetcodeLeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer第 2 版》、《程序员面试金典第 6 版》题解项目地址https://gitcode.com/doocs/leetcode点击查看免费下载本文基于 doocs/leetcode 开源仓库中 228. 汇总区间 一题的官方题解展开讲解如何用双指针在有序无重复数组中一次扫描切分出所有连续区间并给出 Python、Java、C、Go、TypeScript、Rust、C# 七种语言的完整实现对照帮助读者理解这类区间压缩/区间合并问题的通用套路与边界处理。题目回顾把有序数组压缩成最小有序区间列表题目描述原文见 README.md给定一个无重复元素的有序整数数组nums。区间[a,b]是从a到b包含两端的所有整数的集合。要求返回恰好覆盖数组中所有数字的最小有序区间范围列表——也就是说nums的每个元素都恰好被某个区间覆盖且不存在属于某个区间但不属于nums的数字x。列表中的每个区间[a,b]按如下格式输出条件输出格式含义a ! ba-b区间包含多个连续整数a ba单点区间只输出该数字本身示例与约束示例 1输入nums [0,1,2,4,5,7] 输出[0-2,4-5,7]区间切分逻辑[0,2] -- 0-2[4,5] -- 4-5[7,7] -- 7。示例 2输入nums [0,2,3,4,6,8,9] 输出[0,2-4,6,8-9]区间切分逻辑[0,0] -- 0[2,4] -- 2-4[6,6] -- 6[8,9] -- 8-9。约束条件0 nums.length 20-2^31 nums[i] 2^31 - 1nums中的所有值都互不相同nums按升序排列思路分析抓住连续段 相邻差为 1这一关键本题能一次扫描解决依赖两个前提条件数组有序升序排列保证相邻元素的关系可以线性判断元素无重复不会出现相同的值需要归入两个区间的情况。于是恰好覆盖、最小有序区间列表就等价于把数组中所有相邻差为 1 的元素聚合成一段其余元素各自成单点区间。扫描时只需判断nums[j 1] nums[j] 1是否成立成立则说明j与j 1属于同一连续段继续延伸不成立则说明当前段到此结束立即收尾输出。仓库题解文档给出的标准解法是双指针而 Rust 实现则采用了等价的start/prev 游标写法两种写法的时间复杂度一致下面分别展开。方法一双指针仓库官方题解的标准解法算法过程维护两个指针i和j其中i指向当前区间的左端点j负责向右延伸初始化i 0令j i只要j 1 n且nums[j 1] nums[j] 1就把j右移一位直到连续段结束此时[i, j]就是一个完整区间按规则格式化后加入答案令i j 1继续寻找下一个区间直到i n。格式化规则由辅助函数f(i, j)统一处理i j时输出单点nums[i]否则输出nums[i]-nums[j]。复杂度时间复杂度O(n)其中n为数组长度。每个元素最多被j指针访问一次空间复杂度O(1)不计输出答案数组本身。Python3 实现对应源码Solution.pyclass Solution: def summaryRanges(self, nums: List[int]) - List[str]: def f(i: int, j: int) - str: return str(nums[i]) if i j else f{nums[i]}-{nums[j]} i 0 n len(nums) ans [] while i n: j i while j 1 n and nums[j 1] nums[j] 1: j 1 ans.append(f(i, j)) i j 1 return ans要点说明f(i, j)用条件表达式一次性处理单点与区间的两种输出格式内层while的j 1 n是数组越界保护必须先判断外层while每轮结束把i跳到j 1保证每个元素恰好归属一个区间。Java 实现对应源码Solution.javaclass Solution { public ListString summaryRanges(int[] nums) { ListString ans new ArrayList(); for (int i 0, j, n nums.length; i n; i j 1) { j i; while (j 1 n nums[j 1] nums[j] 1) { j; } ans.add(f(nums, i, j)); } return ans; } private String f(int[] nums, int i, int j) { return i j ? nums[i] : String.format(%d-%d, nums[i], nums[j]); } }要点说明for循环的更新表达式i j 1天然承担了跳到下一段起点的工作f方法中对i j分支用nums[i] 完成 int 到 String 的转换非空数组下不会越界。C 实现对应源码Solution.cppclass Solution { public: vectorstring summaryRanges(vectorint nums) { vectorstring ans; auto f { return i j ? to_string(nums[i]) : to_string(nums[i]) - to_string(nums[j]); }; for (int i 0, j, n nums.size(); i n; i j 1) { j i; while (j 1 n nums[j 1] nums[j] 1) { j; } ans.emplace_back(f(i, j)); } return ans; } };要点说明这里用 lambda 表达式f捕获数组并封装格式化逻辑to_string负责数字转字符串ans.emplace_back直接构造string避免多余的拷贝。Go 实现对应源码Solution.gofunc summaryRanges(nums []int) (ans []string) { f : func(i, j int) string { if i j { return strconv.Itoa(nums[i]) } return strconv.Itoa(nums[i]) - strconv.Itoa(nums[j]) } for i, j, n : 0, 0, len(nums); i n; i j 1 { j i for j1 n nums[j1] nums[j]1 { j } ans append(ans, f(i, j)) } return }要点说明Go 版本使用了命名返回值(ans []string)函数体末尾直接return即可返回累积结果strconv.Itoa用于 int 转字符串。TypeScript 实现对应源码Solution.tsfunction summaryRanges(nums: number[]): string[] { const f (i: number, j: number): string { return i j ? ${nums[i]} : ${nums[i]}-${nums[j]}; }; const n nums.length; const ans: string[] []; for (let i 0, j 0; i n; i j 1) { j i; while (j 1 n nums[j 1] nums[j] 1) { j; } ans.push(f(i, j)); } return ans; }要点说明TypeScript 版本利用模板字符串${nums[i]}完成数字到字符串的隐式转换写法最简洁类型标注(i: number, j: number): string保证了格式化函数的入参与返回值类型安全。C# 实现对应源码Solution.cspublic class Solution { public IListstring SummaryRanges(int[] nums) { var ans new Liststring(); for (int i 0, j 0, n nums.Length; i n; i j 1) { j i; while (j 1 n nums[j 1] nums[j] 1) { j; } ans.Add(f(nums, i, j)); } return ans; } public string f(int[] nums, int i, int j) { return i j ? nums[i].ToString() : string.Format({0}-{1}, nums[i], nums[j]); } }要点说明方法返回类型IListstring允许调用方按接口约定使用string.Format({0}-{1}, ...)是 C# 中经典的占位符格式化写法。方法二Rust 变体——start/prev 游标维护当前段对应源码Solution.rsRust 版本没有采用双指针而是用start当前段起点与prev上一个元素两个游标在单次for遍历中维护区间逻辑等价但写法不同impl Solution { #[allow(dead_code)] pub fn summary_ranges(nums: Veci32) - VecString { if nums.is_empty() { return vec![]; } let mut ret Vec::new(); let mut start nums[0]; let mut prev nums[0]; let mut current 0; let n nums.len(); for i in 1..n { current nums[i]; if current ! prev 1 { if start prev { ret.push(start.to_string()); } else { ret.push(start.to_string() - prev.to_string()); } start current; prev current; } else { prev current; } } if start prev { ret.push(start.to_string()); } else { ret.push(start.to_string() - prev.to_string()); } ret } }要点说明显式处理空数组nums.is_empty()时直接返回空VecString避免后续对nums[0]的越界访问遍历从下标 1 开始用current ! prev 1判断连续被打破一旦打破就结算当前段然后以current同时重置start与prev循环结束后还需要再结算一次最后一个段因为最后一段不会在循环体内触发结算分支#[allow(dead_code)]是仓库中 LeetCode 题解的通用标注用于消除未使用代码的编译警告。这种写法与双指针本质上共享同一个判定条件nums[j 1] nums[j] 1只是把区间边界抽象成了游标变量在需要流式处理或不想写两层循环的语言中更为自然。边界情况与正确性验证结合题目约束0 nums.length 20可以从源码结构推演出以下边界场景的处理结果输入期望输出处理路径[]空数组[]外层循环条件i n不成立直接返回空列表Rust 版本额外做了is_empty显式判断[5]单元素[5]内层 while 不进入i j格式化分支输出单点[1,2,3,4,5]全部连续[1-5]内层 while 一直延伸到最后整个数组合并为一个区间[1,3,5]全部孤立[1,3,5]每个元素各自成单点区间端点值-2^31、2^31 - 1按原值输出格式化走字符串拼接不涉及数值运算不存在溢出风险值得注意的一个细节虽然题目约束数组长度很小最多 20但nums[i]的取值范围覆盖整个 32 位有符号整数域因此判断连续时只能使用nums[j 1] nums[j] 1这样的等值比较而不能用诸如nums[i 1] - nums[i] 1之类的写法减法在-2^31 - 1场景下可能触发整数溢出仓库各语言实现均采用等值比较规避了这一隐患。复杂度分析汇总指标双指针方法Python/Java/C/Go/TS/C#start/prev 方法Rust时间复杂度O(n)单次线性扫描每个元素最多被访问常数次O(n)单次for遍历空间复杂度O(1)仅使用若干指针变量与结果数组O(1)仅使用三个游标变量与结果数组两种写法都不依赖额外数据结构无需哈希表、前缀和或排序——因为输入已有序是最优的线性解。小结本题的核心价值在于把区间合并/区间压缩这类问题抽象为一次线性扫描有序 无重复两个前提让连续性退化为相邻元素的等差判定双指针或 start/prev 游标加上一个统一的格式化函数即可在O(n)时间内得到最小有序区间列表。仓库中该题的中英文文档README.md、README_EN.md与七种语言的独立源码文件Solution.py、Solution.java、Solution.cpp、Solution.go、Solution.ts、Solution.cs、Solution.rs均位于solution/0200-0299/0228.Summary Ranges/目录下可直接对照阅读掌握双指针的移动时机与格式化分支判断后类似的合并区间题目如按区间端点排序后合并重叠段也能触类旁通。赞分享示例工程【免费下载链接】leetcodeLeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer第 2 版》、《程序员面试金典第 6 版》题解项目地址https://gitcode.com/doocs/leetcode点击查看免费下载相关推荐LeetCode-Go 题解228. Summary Ranges 有序数组区间压缩的 Go 实现LeetCode Go 题解228. Summary Ranges 有序数组区间压缩的 Go 实现 导读 本篇围绕 LeetCode Go 仓库中 0228.示例工程LeetCode 0228「汇总区间」题解全解析基于有序数组的双指针连续区间合并实战AlgoNote 算法通关手册LeetCode 0228「汇总区间」题解全解析基于有序数组的双指针连续区间合并实战AlgoNote 算法通关手册 本文以 AlgoNote「算法通关手册教程教育Leetcode 区间问题实战Remove Interval 的扫描线一遍切分法Leetcode 区间问题实战Remove Interval 的扫描线一遍切分法 本文围绕 NeetCode 题解库 leetcode 仓库中的 remove示例工程上一篇Files文件管理器终极指南新手必看的100个问题解答下一篇TanStack Table v9 Svelte 列排序Column Ordering完全指南状态管理、API 与拖拽实现创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考