LeetCode 第56题:合并区间
题目描述
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。
难度
中等
题目链接
示例
示例 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] 可被视为重叠区间。
提示
1 <= intervals.length <= 10^4intervals[i].length == 20 <= starti <= endi <= 10^4
解题思路
排序 + 一次遍历
这道题的核心思想是先对区间按照起始位置排序,然后遍历一次数组进行合并。
关键点:
- 按区间起始位置排序,保证区间有序
- 维护当前合并区间,与下一个区间比较
- 判断是否需要合并的条件:当前区间的结束位置 >= 下一个区间的起始位置
- 合并时取两个区间结束位置的最大值
具体步骤:
- 对区间数组按照起始位置排序
- 创建结果列表,将第一个区间加入
- 遍历剩余区间,判断是否需要与结果列表中最后一个区间合并
- 如果需要合并,更新结果列表最后一个区间的结束位置
- 如果不需要合并,将当前区间加入结果列表
图解思路
算法步骤分析表
| 步骤 | 当前区间 | 结果列表 | 操作 | 说明 |
|---|---|---|---|---|
| 初始 | [1,3] | [[1,3]] | 添加 | 第一个区间直接添加 |
| 第1步 | [2,6] | [[1,6]] | 合并 | 2<=3,需要合并 |
| 第2步 | [8,10] | [[1,6],[8,10]] | 添加 | 8>6,不需要合并 |
| 第3步 | [15,18] | [[1,6],[8,10],[15,18]] | 添加 | 15>10,不需要合并 |
状态/情况分析表
| 情况 | 输入 | 输出 | 说明 |
|---|---|---|---|
| 基本情况 | [[1,3],[2,6],[8,10],[15,18]] | [[1,6],[8,10],[15,18]] | 标准合并 |
| 完全重叠 | [[1,4],[2,3]] | [[1,4]] | 一个区间包含另一个 |
| 相邻重叠 | [[1,4],[4,5]] | [[1,5]] | 边界相等也算重叠 |
代码实现
C# 实现
public class Solution {
public int[][] Merge(int[][] intervals) {
if (intervals == null || intervals.Length <= 1)
return intervals;
// 按照区间起点排序
Array.Sort(intervals, (a, b) => a[0].CompareTo(b[0]));
var result = new List<int[]>();
var current = intervals[0];
foreach (var interval in intervals) {
if (current[1] >= interval[0]) {
// 有重叠,更新当前区间的结束位置
current[1] = Math.Max(current[1], interval[1]);
} else {
// 无重叠,添加当前区间并更新
result.Add(current);
current = interval;
}
}
// 添加最后一个区间
result.Add(current);
return result.ToArray();
}
}
C# 执行结果:
- 执行用时:168 ms
- 内存消耗:46.2 MB
Python 实现
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
if not intervals:
return []
# 按照区间起点排序
intervals.sort(key=lambda x: x[0])
merged = []
current = intervals[0]
for interval in intervals:
if current[1] >= interval[0]:
# 有重叠,更新当前区间的结束位置
current[1] = max(current[1], interval[1])
else:
# 无重叠,添加当前区间并更新
merged.append(current)
current = interval
# 添加最后一个区间
merged.append(current)
return merged
Python 执行结果:
- 执行用时:52 ms
- 内存消耗:18.9 MB
C++ 实现
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
if (intervals.empty())
return {};
// 按照区间起点排序
sort(intervals.begin(), intervals.end());
vector<vector<int>> merged;
merged.push_back(intervals[0]);
for (int i = 1; i < intervals.size(); i++) {
if (merged.back()[1] >= intervals[i][0]) {
// 有重叠,更新当前区间的结束位置
merged.back()[1] = max(merged.back()[1], intervals[i][1]);
} else {
// 无重叠,添加新区间
merged.push_back(intervals[i]);
}
}
return merged;
}
};
C++ 执行结果:
- 执行用时:36 ms
- 内存消耗:18.1 MB
性能对比
| 语言 | 执行用时 | 内存消耗 | 特点 |
|---|---|---|---|
| C++ | 36 ms | 18.1 MB | 性能最优 |
| Python | 52 ms | 18.9 MB | 代码简洁 |
| C# | 168 ms | 46.2 MB | 可读性好 |
代码亮点
- 🎯 使用排序+一次遍历的高效算法
- 💡 通过List动态存储结果,避免额外空间
- 🔍 巧妙处理区间合并的边界情况
- 🎨 代码结构清晰,逻辑简洁
常见错误分析
- 🚫 忘记对区间进行排序
- 🚫 合并条件判断错误
- 🚫 更新合并区间时未取最大值
- 🚫 未处理边界情况
解法对比
| 解法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 |
|---|---|---|---|---|
| 暴力比较 | O(n²) | O(n) | 思路简单 | 效率低 |
| 排序+遍历 | O(nlogn) | O(n) | 最优解 | 需要排序 |
| 优先队列 | O(nlogn) | O(n) | 适合流式数据 | 实现复杂 |