编码母题 / P0 / 经典算法 / 02
合并重叠区间
重要程度必练难度中等预计完成20–30 分钟
题目是什么
每个闭区间 [start,end] 均满足 start <= end,且端点为有限数字。输入若干区间,合并重叠项并按起点升序返回;端点相接也算重叠。不得修改原数组或内部区间,空数组返回 []。
输入输出示例
输入 [[1,3],[2,6],[8,10],[10,18]],输出 [[1,6],[8,18]];输入 [],输出 []。
练习起手式与验证
/*
* 题目:合并重叠区间
* 要求:每个闭区间 [start,end] 均满足 start <= end,且端点为有限数字。输入若干区间,合并重叠项并按起点升序返回;端点相接也算重叠。不得修改原数组或内部区间,空数组返回 []。
* 示例:输入 [[1,3],[2,6],[8,10],[10,18]],输出 [[1,6],[8,18]];输入 [],输出 []。
*/
function mergeIntervals(intervals) {}
console.log(mergeIntervals([[1, 3], [2, 6], [8, 10], [10, 18]])); // 预期:[[1, 6], [8, 18]]
console.log(mergeIntervals([])); // 预期:[]
console.log(mergeIntervals([[1, 4], [2, 3]])); // 预期:[[1, 4]]
console.log(mergeIntervals([[1, 2], [2, 3]])); // 预期:[[1, 3]]
/*
* 题目:合并重叠区间
* 要求:每个闭区间 [start,end] 均满足 start <= end,且端点为有限数字。输入若干区间,合并重叠项并按起点升序返回;端点相接也算重叠。不得修改原数组或内部区间,空数组返回 []。
* 示例:输入 [[1,3],[2,6],[8,10],[10,18]],输出 [[1,6],[8,18]];输入 [],输出 []。
*/
function mergeIntervals(intervals) {}
console.log(mergeIntervals([[1, 3], [2, 6], [8, 10], [10, 18]])); // 预期:[[1, 6], [8, 18]]
console.log(mergeIntervals([])); // 预期:[]
console.log(mergeIntervals([[1, 4], [2, 3]])); // 预期:[[1, 4]]
console.log(mergeIntervals([[1, 2], [2, 3]])); // 预期:[[1, 3]]解题思路与实现
展开解题思路
先复制每个区间并按起点排序。扫描时若当前区间与下一项重叠,就把合并结果写到下一项,让它继续向后参与合并;否则提交当前区间。
function mergeIntervals(intervals) {
// 输入保证每个区间满足 start <= end;复制两层以免排序和合并修改入参。
const sorted = intervals
.map(([start, end]) => [start, end])
.sort((a, b) => a[0] - b[0]);
const result = [];
for (let i = 0; i < sorted.length; i++) {
const current = sorted[i];
const next = sorted[i + 1];
if (next && current[1] >= next[0]) {
// 把合并段交给下一轮,包含区间和链式重叠都不会漏掉。
sorted[i + 1] = [current[0], Math.max(current[1], next[1])];
} else {
result.push(current);
}
}
return result;
}
// 三组以上可直接运行的验证。
console.log(mergeIntervals([[1, 3], [2, 6], [8, 10], [10, 18]])); // 预期:[[1, 6], [8, 18]]
console.log(mergeIntervals([])); // 预期:[]
console.log(mergeIntervals([[1, 4], [2, 3]])); // 预期:[[1, 4]]
console.log(mergeIntervals([[1, 2], [2, 3]])); // 预期:[[1, 3]]
const original = [[8, 10], [1, 3], [2, 6]];
console.log(mergeIntervals(original)); // 预期:[[1, 6], [8, 10]]
console.log(original); // 预期:[[8, 10], [1, 3], [2, 6]]
function mergeIntervals(intervals) {
// 输入保证每个区间满足 start <= end;复制两层以免排序和合并修改入参。
const sorted = intervals
.map(([start, end]) => [start, end])
.sort((a, b) => a[0] - b[0]);
const result = [];
for (let i = 0; i < sorted.length; i++) {
const current = sorted[i];
const next = sorted[i + 1];
if (next && current[1] >= next[0]) {
// 把合并段交给下一轮,包含区间和链式重叠都不会漏掉。
sorted[i + 1] = [current[0], Math.max(current[1], next[1])];
} else {
result.push(current);
}
}
return result;
}
// 三组以上可直接运行的验证。
console.log(mergeIntervals([[1, 3], [2, 6], [8, 10], [10, 18]])); // 预期:[[1, 6], [8, 18]]
console.log(mergeIntervals([])); // 预期:[]
console.log(mergeIntervals([[1, 4], [2, 3]])); // 预期:[[1, 4]]
console.log(mergeIntervals([[1, 2], [2, 3]])); // 预期:[[1, 3]]
const original = [[8, 10], [1, 3], [2, 6]];
console.log(mergeIntervals(original)); // 预期:[[1, 6], [8, 10]]
console.log(original); // 预期:[[8, 10], [1, 3], [2, 6]]
时间复杂度:O(n log n)。复制与扫描各为 O(n),按起点排序占主导。
空间复杂度:O(n)。区间副本和返回结果各最多保存 n 个区间。