56. 合并区间

中等 含交互动画

学完这道你应该能: 说清它为什么用「合并」,而不是死记步骤; 合上页面,自己默写出核心代码; 用 30 秒把思路讲清楚。

AI 私教 · 就着本题动画讲
卡住了不用硬扛,按 8 步把这题真正走通。
从读题、试解、找法到手写代码,边问边打勾,适合第一次系统学算法的同学。

题目描述

给若干区间 [start, end],把所有有重叠的合并掉,返回合并后互不重叠的区间列表。

输入 = [[1,3],[2,6],[8,10],[15,18]]
输出 = [[1,6],[8,10],[15,18]]

思路解析

区间顺序乱时两两比,不光 O(n²) 慢,还容易漏掉「A 和 B 合并后,合出来的大区间又跟 C 重叠」这种连环情况,逻辑很绕。

转折:排序后,能重叠的区间必然相邻。证明很直观——若 A 起点 ≤ B 起点、A 和 C 重叠,那起点夹在中间的 B 一定也跟 A 沾边。于是只需从左到右扫一遍,拿当前区间跟「结果里最后一段 merged」比就够了:当前起点 ≤ merged 的终点就合并(终点取更大的),否则新开一段。一遍 O(n) 搞定,再不用两两回头比。

已按起点排序:本例输入起点恰好递增,排序后顺序不变:1、2、8、15。结果列表 merged 现在是空的。

处理 [1,3]:第一个 [1,3] 没东西可比,直接放进结果。当前合并段 [s,e] = [1,3]。

处理 [2,6] · 判重叠:[2,6] 的起点 2 没超过末段 [1,3] 的终点 3 → 重叠。该合并,而不是新开。

处理 [2,6] · 合并:合并时终点取更大的:max(3, 6)=6,末段被拉长成 [1,6]。注意起点 1 不变,只把终点往右拉。

处理 [8,10] · 负例:负例分支来了:[8,10] 的起点 8 已经超过末段 [1,6] 的终点 6,接不上 → 不合并,把 [8,10] 当新的一段 append 进结果。

[8,10] 新开后:现在结果末段换成了 [8,10],接下来的区间只需要跟它比。前面合好的 [1,6] 已经定型,不再回头看。

处理 [15,18] · 判重叠:轮到最后一个 [15,18]。还是老规矩:拿它的起点 15,跟结果末段 [8,10] 的终点 10 比一比。

处理 [15,18] · 负例:[15,18] 的起点 15 又超过末段 [8,10] 的终点 10,同样接不上,再新开一段。扫完,结果 = [[1,6],[8,10],[15,18]],三段互不重叠。

区间题的万能第一步——按端点排序。排完之后,绝大多数区间问题都能一遍扫描搞定,每个新区间只跟「结果里最后一段」打交道。

参考代码(Python)

Python
1def merge(intervals):
2    intervals.sort(key=lambda x: x[0])      # 关键:先按起点排序
3    merged = []
4    for s, e in intervals:
5        if merged and s <= merged[-1][1]:   # 和末段重叠
6            merged[-1][1] = max(merged[-1][1], e)  # 合并:终点取 max
7        else:
8            merged.append([s, e])           # 接不上,新开一段
9    return merged

复杂度分析

  • 时间复杂度:O(n log n) —— 排序是 O(n log n) 主导;后面只扫一遍是 O(n)
  • 空间复杂度:O(n) —— merged 结果列表最坏存下全部 n 个区间

套路模板

骨架记牢:排序 → 扫描 → 比末段终点决定合并还是新开。合并时终点一定取 max,别忘了。

Python
1intervals.sort(key=lambda x: x[0])
2merged = []
3for s, e in intervals:
4    if merged and s <= merged[-1][1]:
5        merged[-1][1] = max(merged[-1][1], e)
6    else:
7        merged.append([s, e])

易错点

  • 错误写法:不排序直接扫正确写法:先 intervals.sort(key=lambda x: x[0])(不排序时重叠区间未必相邻,一遍扫描会漏合并,比如 [[1,4],[5,6],[2,3]] 不排序就合不上 [1,4] 和 [2,3])
  • 错误写法:合并时直接 merged[-1][1] = e正确写法:merged[-1][1] = max(merged[-1][1], e)(当前区间可能被末段完全包住(e 更小),不取 max 会把终点改短,丢掉范围)
  • 错误写法:判重叠用 s < merged[-1][1](漏等号)正确写法:s <= merged[-1][1]([1,3] 和 [3,5] 端点相接也算重叠,漏等号会错拆成两段)

以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握

下一题 →560. 和为 K 的子数组 ← 返回题库