2026/3/29 2:55:29
网站建设
项目流程
阜阳手机端网站建设,搜索引擎优化简称seo,微网站解决方案,哪里有零基础网站建设教学培训#x1f525;【LeetCode热题100精讲】Java实现「合并区间」#xff1a;排序贪心经典应用#xff0c;深入剖析区间处理算法 关键词#xff1a;LeetCode 56、合并区间、区间合并、排序、贪心算法、面试高频题、LeetCode热题100 适用人群#xff1a;准备技术面试的程序员、算…【LeetCode热题100精讲】Java实现「合并区间」排序贪心经典应用深入剖析区间处理算法关键词LeetCode 56、合并区间、区间合并、排序、贪心算法、面试高频题、LeetCode热题100适用人群准备技术面试的程序员、算法爱好者、Java开发者、计算机专业学生阅读时长约25分钟全文超9000字含完整代码、图解、复杂度分析与实战建议在算法面试中区间处理问题是一类非常常见且重要的题型。而「合并区间」LeetCode 第56题正是这类问题中的经典代表作被广泛应用于各类技术面试中。本文将带你从问题本质出发系统性地掌握这道 LeetCode 热题100 中的重要题目。我们将深入理解区间合并的核心逻辑实现标准 O(n log n) 时间复杂度的最优解分析代码细节、边界条件与性能瓶颈提供多种优化思路与调试技巧探讨其在实际工程中的应用场景构建相关题目知识网络。无论你是初次接触区间问题还是希望深化对贪心算法的理解本文都将为你提供清晰、严谨、可落地的解决方案。 一、原题回顾题目描述以数组intervals表示若干个区间的集合其中单个区间为intervals[i] [starti, endi]。请你合并所有重叠的区间并返回一个不重叠的区间数组该数组需恰好覆盖输入中的所有区间。注意区间[a, b]和[b, c]被视为重叠区间端点相接也算重叠。示例示例 1输入intervals [[1,3],[2,6],[8,10],[15,18]] 输出[[1,6],[8,10],[15,18]] 解释区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].示例 2输入intervals [[1,4],[4,5]] 输出[[1,5]] 解释区间 [1,4] 和 [4,5] 可被视为重叠区间。示例 3输入intervals [[4,7],[1,4]] 输出[[1,7]] 解释区间 [1,4] 和 [4,7] 可被视为重叠区间。约束条件1 intervals.length 10⁴intervals[i].length 20 starti endi 10⁴ 二、问题分析与核心洞察2.1 什么是重叠区间两个区间[a, b]和[c, d]重叠的条件是b c且d a但在实际处理中如果我们先按左端点排序那么只需要检查previous_end current_start因为排序后保证了previous_start current_start。2.2 直观思路暴力两两合并最朴素的想法是遍历所有区间对如果重叠就合并重复此过程直到无法再合并。时间复杂度O(n³)最坏情况下每次合并都要重新扫描实现复杂需要处理合并后的区间与其他区间的重叠显然这不是最优解。2.3 核心洞察排序 贪心✅关键观察如果我们将区间按左端点排序那么可以合并的区间一定是连续的为什么假设存在三个区间 A、B、C按左端点排序如果 A 和 C 可以合并但 A 和 B、B 和 C 不能合并那么A.end B.startB.end C.start但 A.end ≥ C.start因为 A 和 C 可合并这导致矛盾A.end B.start ≤ B.end C.start ≤ A.end因此所有可合并的区间在排序后必然是连续的。 三、解法详解排序 贪心O(n log n)3.1 算法步骤排序按区间的左端点升序排序初始化结果集将第一个区间加入结果集遍历剩余区间如果当前区间的左端点 结果集中最后一个区间的右端点 → 不重叠直接添加否则 → 重叠更新结果集中最后一个区间的右端点为max(原有右端点, 当前右端点)3.2 正确性证明如前所述通过反证法可以证明排序后所有可合并的区间都是连续的。因此我们只需要一次线性扫描即可完成合并。3.3 完整代码实现标准版importjava.util.*;classSolution{publicint[][]merge(int[][]intervals){// 边界检查if(intervalsnull||intervals.length0){returnnewint[0][0];}// Step 1: 按左端点升序排序Arrays.sort(intervals,(a,b)-Integer.compare(a[0],b[0]));// Step 2: 初始化结果列表Listint[]mergednewArrayList();// Step 3: 遍历所有区间for(int[]interval:intervals){intcurrentStartinterval[0];intcurrentEndinterval[1];// 如果结果集为空或者当前区间与最后一个区间不重叠if(merged.isEmpty()||merged.get(merged.size()-1)[1]currentStart){merged.add(newint[]{currentStart,currentEnd});}else{// 重叠更新最后一个区间的右端点merged.get(merged.size()-1)[1]Math.max(merged.get(merged.size()-1)[1],currentEnd);}}// 转换为数组返回returnmerged.toArray(newint[merged.size()][]);}}关键点使用Integer.compare(a[0], b[0])而不是a[0] - b[0]避免整数溢出问题。 四、代码深度解析4.1 排序的重要性排序是整个算法的基础。如果不排序我们就无法保证重叠区间是连续的也就无法用一次线性扫描完成合并。排序前[[4,7],[1,4]]→ 无法直接判断是否重叠排序后[[1,4],[4,7]]→ 显然可以合并4.2 重叠判断条件merged.get(merged.size()-1)[1]currentStart如果为true不重叠添加新区间如果为false重叠合并区间注意这里使用而不是因为题目说明[1,4]和[4,5]是重叠的。4.3 右端点更新策略当区间重叠时我们取两个区间右端点的最大值Math.max(merged.get(merged.size()-1)[1],currentEnd)这是因为可能存在包含关系如[1,6]和[2,4]合并后仍为[1,6]。4.4 数组转换细节returnmerged.toArray(newint[merged.size()][]);这是将Listint[]转换为int[][]的标准方式。注意括号的位置new int[merged.size()][]而不是new int[][merged.size()]。 五、复杂度分析项目分析时间复杂度O(n log n)- 排序O(n log n)- 线性扫描O(n)-主导项为排序空间复杂度O(log n)- 排序所需的栈空间Arrays.sort 使用双轴快排- 结果数组不计入额外空间✅这是理论最优解任何基于比较的排序算法都需要 Ω(n log n) 时间。❓ 六、常见问题解答FAQQ1为什么端点相接的区间算重叠答题目明确说明[1,4]和[4,5]是重叠的。在数学上这称为闭区间包含端点。实际应用中如日程安排结束时间等于开始时间通常表示连续事件。Q2能否不使用额外的 List直接在原数组上操作答理论上可以但实现复杂且容易出错。因为合并后区间数量减少需要处理数组缩容问题。使用List更清晰、安全。Q3排序时为什么要用Integer.compare而不是减法答考虑极端情况a[0] Integer.MIN_VALUE,b[0] Integer.MAX_VALUE此时a[0] - b[0]会溢出导致错误的排序结果。Integer.compare安全处理所有情况。Q4如果区间数量很大10⁶有什么优化空间答对于超大数据集可以考虑使用更高效的排序算法如基数排序如果数值范围有限并行处理但合并步骤难以并行化流式处理如果数据是流式的⚡ 七、优化思路与进阶实现7.1 预分配 List 容量// 预分配容量避免动态扩容Listint[]mergednewArrayList(intervals.length);虽然最坏情况下结果数组长度仍为n但平均情况下会减少扩容次数。7.2 手动实现排序针对特定场景如果区间数值范围有限如0 start, end 10⁴可以使用计数排序或桶排序将时间复杂度降为 O(n)。// 计数排序示例简化版publicvoidcountingSort(int[][]intervals){intmaxVal10000;ListInteger[]bucketsnewList[maxVal1];// 初始化桶for(inti0;imaxVal;i){buckets[i]newArrayList();}// 分配到桶中for(inti0;iintervals.length;i){buckets[intervals[i][0]].add(i);}// 重新排列intindex0;for(inti0;imaxVal;i){for(intoriginalIndex:buckets[i]){intervals[index]intervals[originalIndex];}}}⚠️注意这种方法只在数值范围较小时有效且实现复杂。7.3 处理开区间的情况如果题目要求处理开区间端点相接不算重叠只需修改判断条件// 开区间端点相接不算重叠if(merged.isEmpty()||merged.get(merged.size()-1)[1]currentStart){merged.add(newint[]{currentStart,currentEnd});}else{// 合并}️ 八、调试技巧与实战建议8.1 调试建议打印排序后的区间System.out.println(Sorted intervals: Arrays.deepToString(intervals));小规模测试单个区间[[1,2]]→[[1,2]]完全重叠[[1,5],[2,3]]→[[1,5]]完全不重叠[[1,2],[3,4]]→[[1,2],[3,4]]边界用例端点相接[[1,4],[4,5]]包含关系[[1,6],[2,4]]乱序输入[[4,7],[1,4]]8.2 代码健壮性增强// 输入验证if(intervalsnull){thrownewIllegalArgumentException(Intervals cannot be null);}for(int[]interval:intervals){if(intervalnull||interval.length!2){thrownewIllegalArgumentException(Each interval must have exactly 2 elements);}if(interval[0]interval[1]){thrownewIllegalArgumentException(Start must be end);}}8.3 面试答题策略先说暴力思路指出 O(n²) 或更高复杂度的问题。提出排序思想解释为什么排序后重叠区间连续。写出代码强调边界处理和溢出防护。分析复杂度讨论是否有优化空间。 九、数据结构与算法知识点回顾9.1 贪心算法Greedy Algorithm核心思想每一步都做出局部最优选择希望最终得到全局最优解适用条件贪心选择性质局部最优能导致全局最优最优子结构问题的最优解包含子问题的最优解本题应用每次都将当前区间与结果集最后一个区间合并如果可能这是局部最优选择9.2 排序算法比较排序基于元素比较的排序时间复杂度下界为 Ω(n log n)非比较排序如计数排序、基数排序在特定条件下可达到 O(n)Java 的 Arrays.sort基本类型双轴快速排序Dual-Pivot Quicksort对象类型Timsort归并排序 插入排序的混合9.3 区间问题通用技巧排序按左端点或右端点排序是处理区间问题的第一步扫描线从左到右扫描维护当前状态事件点将区间拆分为开始事件和结束事件 十、实际开发中的应用场景虽然“合并区间”看似理论化但其思想广泛应用于日程安排系统合并重叠的会议时间显示可用时间段。数据库查询优化合并重叠的范围查询条件减少 I/O 操作。内存管理合并相邻的空闲内存块减少内存碎片。网络路由合并重叠的 IP 地址范围优化路由表。基因组学合并重叠的基因片段构建完整的基因序列。案例某在线教育平台需要检测教师的日程冲突。教师提交多个课程时间段系统需要合并重叠的时间段并检查是否与其他教师冲突。这正是“合并区间”问题的直接应用。 十一、相关题目推荐题号题目关联点57插入区间在已合并的区间中插入新区间435无重叠区间删除最少区间使剩余区间不重叠452用最少数量的箭引爆气球区间调度问题763划分字母区间字符串版本的区间问题✅学习路径建议56 → 57 → 435 → 452 → 763 十二、面试官提问环节Q1如果要求保留原始区间的引用信息如ID如何修改答可以创建一个包装类包含区间和原始IDclassIntervalWithId{intstart,end,id;// constructor, getters...}排序时按start排序合并时同时记录所有相关的ID。Q2如果区间数量动态增加如何高效维护合并结果答可以使用平衡二叉搜索树如TreeMap存储已合并的区间。插入新区间时找到可能重叠的区间通过 floor/ceiling 操作合并所有重叠区间更新树结构时间复杂度O(k log n)其中 k 是受影响的区间数量。Q3能否用差分数组解决这个问题答差分数组适合处理区间更新和点查询但不适合合并区间这种需要保持区间结构的问题。差分数组会丢失原始区间的边界信息。Q4如果区间是三维的如 [x1,x2,y1,y2,z1,z2]如何处理答三维区间合并是 NP-hard 问题没有多项式时间的精确解法。通常采用启发式算法或近似算法如 R-tree 索引结构。 十三、总结与延伸13.1 核心收获排序是处理区间问题的关键第一步贪心策略排序后一次扫描即可完成合并边界处理端点相接算重叠右端点取最大值13.2 面试答题策略明确问题确认重叠定义闭区间 vs 开区间展示思路从暴力到排序贪心体现思考过程写出代码注意空值检查、溢出防护、边界条件分析复杂度强调 O(n log n) 的合理性讨论扩展提及实际应用场景和变种问题13.3 延伸思考如果要求返回合并操作的详细过程记录哪些区间被合并如果区间有权重要求合并后权重最大的区间如果数据是流式的如何实时维护合并结果 结语「合并区间」不仅是一道算法题更是工程思维的体现通过排序和贪心将复杂的区间重叠问题简化为一次线性扫描。希望本文能帮助你深刻理解区间处理的核心思想掌握排序贪心的通用解题模板在面试中从容应对类似问题。记住算法的本质不是背模板而是理解思想灵活运用。 互动邀请你在面试中遇到过这道题吗有更好的解法或优化建议欢迎在评论区分享 如果本文对你有帮助请点赞、收藏、关注支持原创深度技术文章附录完整可运行代码含测试用例importjava.util.*;publicclassMergeIntervals{publicstaticvoidmain(String[]args){SolutionsolnewSolution();// Test case 1int[][]intervals1{{1,3},{2,6},{8,10},{15,18}};System.out.println(Arrays.deepToString(sol.merge(intervals1)));// [[1,6],[8,10],[15,18]]// Test case 2int[][]intervals2{{1,4},{4,5}};System.out.println(Arrays.deepToString(sol.merge(intervals2)));// [[1,5]]// Test case 3int[][]intervals3{{4,7},{1,4}};System.out.println(Arrays.deepToString(sol.merge(intervals3)));// [[1,7]]// Edge case: single intervalint[][]intervals4{{1,2}};System.out.println(Arrays.deepToString(sol.merge(intervals4)));// [[1,2]]}staticclassSolution{publicint[][]merge(int[][]intervals){if(intervalsnull||intervals.length0){returnnewint[0][0];}Arrays.sort(intervals,(a,b)-Integer.compare(a[0],b[0]));Listint[]mergednewArrayList();for(int[]interval:intervals){intcurrentStartinterval[0];intcurrentEndinterval[1];if(merged.isEmpty()||merged.get(merged.size()-1)[1]currentStart){merged.add(newint[]{currentStart,currentEnd});}else{merged.get(merged.size()-1)[1]Math.max(merged.get(merged.size()-1)[1],currentEnd);}}returnmerged.toArray(newint[merged.size()][]);}}}✅ 所有代码均通过 LeetCode 官方测试可直接提交。字数统计约 9,200 字不含代码注释最后更新2026年1月作者一位热爱算法与工程实践的 Java 开发者