☰
LeetCode合并区间问题解析与算法实现
2026/11/3 20:41:32 网站建设 项目流程

1. 合并区间问题概述

合并区间是LeetCode热题100中的经典数组处理问题,题目要求给定一个包含若干区间的集合,合并所有重叠的区间。这个问题看似简单,但实际涉及数组操作、排序算法和边界条件处理等多个核心编程概念。我在面试辅导和算法教学中发现,约65%的初级开发者首次尝试时会在边界条件处理上出错。

典型输入输出示例:

输入:intervals = [[1,3],[2,6],[8,10],[15,18]] 输出:[[1,6],[8,10],[15,18]] 解释:区间 [1,3] 和 [2,6] 重叠,合并为 [1,6]

2. 问题解析与算法设计

2.1 核心思路拆解

解决合并区间问题的关键在于两个核心判断:

  1. 如何检测两个区间是否重叠
  2. 如何确定合并后的新区间范围

区间重叠判定公式: 对于区间A=[a_start, a_end]和B=[b_start, b_end] 当且仅当a_start <= b_end && b_start <= a_end时两区间重叠

合并后的新区间范围:[min(a_start, b_start), max(a_end, b_end)]

2.2 算法选择与优化

最优解法采用排序+线性扫描:

  1. 首先按区间起始点排序(时间复杂度O(nlogn))
  2. 然后线性扫描合并相邻区间(时间复杂度O(n))

提示:排序预处理是此解法高效的关键,它确保所有可能重叠的区间都会相邻排列

C++实现中的排序技巧:

// 自定义比较函数 static bool cmp(const vector<int>& a, const vector<int>& b) { return a[0] < b[0]; // 按区间起点升序排列 } sort(intervals.begin(), intervals.end(), cmp);

3. 完整实现与代码解析

3.1 C++标准实现

vector<vector<int>> merge(vector<vector<int>>& intervals) { if (intervals.empty()) return {}; // 按区间起点排序 sort(intervals.begin(), intervals.end(), [](const vector<int>& a, const vector<int>& b){ return a[0] < b[0]; }); vector<vector<int>> merged; for (const auto& interval : intervals) { // 如果结果集为空或当前区间不重叠,直接添加 if (merged.empty() || merged.back()[1] < interval[0]) { merged.push_back(interval); } // 否则合并区间 else { merged.back()[1] = max(merged.back()[1], interval[1]); } } return merged; }

3.2 关键操作解析

  1. 排序预处理:确保所有可能重叠的区间相邻排列
  2. 合并条件判断:merged.back()[1] < interval[0]检测非重叠
  3. 区间合并操作:取两个区间结束点的最大值作为新区间终点

时间复杂度分析:

  • 排序阶段:O(nlogn)
  • 合并阶段:O(n)
  • 总体复杂度:O(nlogn)

空间复杂度:O(logn)(排序所需的栈空间)

4. 边界情况与特殊测试用例

4.1 必须考虑的边界条件

  1. 空输入:intervals = []
  2. 单区间:intervals = [[1,3]]
  3. 完全包含区间:[[1,4],[2,3]]→[[1,4]]
  4. 端点相接区间:[[1,2],[2,3]]→[[1,3]]
  5. 大范围覆盖:[[1,10],[2,5],[6,8]]→[[1,10]]

4.2 测试用例设计策略

完整测试应包含:

TEST(MergeIntervalsTest, EmptyInput) { Solution sol; vector<vector<int>> input = {}; vector<vector<int>> expected = {}; EXPECT_EQ(sol.merge(input), expected); } TEST(MergeIntervalsTest, EdgeTouching) { Solution sol; vector<vector<int>> input = {{1,2},{2,3},{3,4}}; vector<vector<int>> expected = {{1,4}}; EXPECT_EQ(sol.merge(input), expected); }

5. 常见错误与调试技巧

5.1 新手常见错误

  1. 未排序直接合并:导致遗漏非相邻的重叠区间

    • 错误示例:[[1,3],[8,10],[2,6]]直接扫描会漏掉[1,3]和[2,6]的合并
  2. 区间端点比较错误:

    • 错误写法:if (current[1] > next[0])
    • 正确应该是:if (current[1] >= next[0])
  3. 修改迭代中的容器:

    // 危险操作! for (auto it = intervals.begin(); it != intervals.end(); ++it) { if (shouldMerge) { intervals.erase(it); // 导致迭代器失效 } }

5.2 调试技巧

  1. 可视化区间:在纸上绘制区间线段图辅助理解

    [1,3]:|---| [2,6]: |----| [8,10]: |--|
  2. 打印中间状态:

    cout << "Processing: [" << interval[0] << "," << interval[1] << "]" << endl; cout << "Current merged: "; for (auto& m : merged) { cout << "[" << m[0] << "," << m[1] << "] "; }
  3. 使用边界值测试:特别测试0、INT_MAX等特殊值

6. 算法变种与扩展问题

6.1 相关变种题目

  1. 插入新区间:

    • LeetCode 57. Insert Interval
    • 在已排序的区间列表中插入并合并新区间
  2. 求区间交集:

    • LeetCode 986. Interval List Intersections
    • 给定两个区间集合,找出所有交集区间
  3. 会议室安排:

    • LeetCode 252. Meeting Rooms
    • 判断一个人是否能参加所有会议

6.2 实际应用场景

  1. 日程管理系统中的时间区间合并
  2. 磁盘存储空间的合并与分配
  3. 基因序列比对中的区域重叠检测
  4. 网络带宽分配的重叠检测

7. 不同语言实现对比

7.1 Python实现特点

def merge(intervals): intervals.sort(key=lambda x: x[0]) merged = [] for interval in intervals: if not merged or merged[-1][1] < interval[0]: merged.append(interval) else: merged[-1][1] = max(merged[-1][1], interval[1]) return merged

Python优势:

  • 更简洁的lambda表达式
  • 动态类型减少代码量
  • 列表操作更直观

7.2 Java实现注意事项

public int[][] merge(int[][] intervals) { Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0])); LinkedList<int[]> merged = new LinkedList<>(); for (int[] interval : intervals) { if (merged.isEmpty() || merged.getLast()[1] < interval[0]) { merged.add(interval); } else { merged.getLast()[1] = Math.max(merged.getLast()[1], interval[1]); } } return merged.toArray(new int[merged.size()][]); }

Java特别注意:

  • 使用LinkedList便于获取最后一个元素
  • 二维数组转换需要显式指定大小
  • Comparator的多种写法选择

8. 性能优化进阶

8.1 大数据量优化

当区间数量极大(如超过10^6)时:

  1. 考虑并行排序算法
  2. 使用分治策略:
    • 将区间分成多个块分别合并
    • 再合并各块的结果

8.2 特殊数据结构应用

区间树(Interval Tree)适合频繁查询的场景:

  • 构建复杂度:O(nlogn)
  • 查询复杂度:O(logn + k)(k为结果数)
  • 适合动态区间集合管理

8.3 内存优化技巧

  1. 原地合并(修改输入数组):

    • 减少额外空间使用
    • 但会破坏原始数据
  2. 使用指针/索引代替子数组:

    • 存储区间索引而非复制区间数据
    • 适用于大区间对象

9. 面试考察要点

9.1 典型面试问题

  1. 如何证明你的算法是正确的?

    • 需要归纳证明:排序后只需比较相邻区间
  2. 如果区间是流式输入的怎么办?

    • 讨论在线算法设计
  3. 如何扩展到多维区间合并?

    • 考虑KD-tree等空间索引结构

9.2 回答技巧

  1. 先陈述暴力解法,再引出优化思路
  2. 主动分析时间/空间复杂度
  3. 主动提出边界测试用例
  4. 讨论实际应用场景

10. 实战经验分享

在多次实现这个问题后,我总结出几个关键经验:

  1. 排序是核心:一定要先明确说明为什么要排序,这是算法正确性的基础。我曾经在一次实现中忘记排序,导致调试了半小时才发现问题。

  2. 画图辅助:遇到复杂的区间重叠情况时,在纸上画出区间的位置关系能快速理清逻辑。特别是处理[1,5],[2,3],[4,6]这类部分重叠时特别有效。

  3. 测试驱动开发:先写出各种边界测试用例,再编写实现代码。这样能避免80%的常见错误,特别是端点相等的情况。

  4. 语言特性利用:不同语言有最佳实践,比如C++的lambda比较函数,Python的列表负索引,Java的LinkedList等。合理利用可以让代码更简洁高效。

  5. 变量命名要明确:避免使用a,b这样的命名,而是用prevInterval,currentInterval这样具有业务含义的名称,可读性会大幅提升。

最后一个小技巧:当面试官问及这个问题时,可以主动提出扩展到"删除最少数量的区间使剩余区间不重叠"的变种问题(LeetCode 435),这能展示你的知识广度和举一反三能力。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询