实现思路
1 思考流程:
1.1 最简数据的 暴力解法: 每一项都 需要和 其他的每一项比较,复杂度是 O(n^2)
1.2 如何减少 需要比较的个数==> 让重叠区间相邻==> 只需要比较相邻区间==> 顺序处理
1.3 如何让重叠区间相邻==> 按开始位置排序
类比理解: 想象你在整理一堆纸条,每个纸条上写着时间段 自然而然会先按开始时间排序,再看哪些时间段重叠
1.4 什么排序后只看相邻的就可以了==>
想象三个区间,按开始位置排好序:
- A: [1,5]
- B: [2,4]
- C: [3,7]
如果 A 能和 C 合并,那 B 一定也要参与合并,因为:
- A 的结束位置是 5
- C 的开始位置是 3
- 既然 A 和 C 能合并,说明 5 ≥ 3
- 而 B 的开始位置是 2,比 3 还小
所以 B 一定也会和 A 重叠
2 总结思维技巧:
- 当遇到区间类问题/数据杂乱 时,排序常常是简化问题的第一步,因为它能把杂乱的关系变得有序,使问题变得可处理
3 合并区间 普遍性:
- 按开始位置排序,以保证重叠区间是相邻的==> 从而实现可以 顺序处理,减少 需要比较的元素个数
- 判断 区间是否重叠,重叠则合并,否则 直接加入结果数组
参考文档
代码实现
1 方法1: 排序 + 重叠区间合并,时间复杂度 O(nlogn) 空间复杂度O(n)
function merge(intervals) {
if(intervals.length <= 1) return intervals;
// 按开始位置排序区间,从而保证重叠区间是相邻的
intervals.sort((a, b) => a[0] - b[0]);
let res = [intervals[0]];
// 多执行一次intervals[0]也不要紧
for (const [start, end] of intervals) {
const [preStart, preEnd] = res[res.length - 1];
// 无重叠,直接加入到结果内
if (start > preEnd) {
res.push([start, end]);
} else {
// 有重叠(比较 cur区间的起始位置 和 pre区间的结束位置)==> 获取左右边界值
res.pop();
res.push([Math.min(start, preStart), Math.max(end, preEnd)]);
}
}
return res;
}