功能说明
该示例接收若干闭区间并输出不重叠的最小覆盖集合。两个区间只要满足后一个起点小于等于前一个终点就合并,因此恰好相邻的 [1,3] 与 [3,5] 会变成 [1,5];完全包含的区间会被较大终点吸收。常用于合并时间段、IP 范围、日志窗口或资源占用区间。输入不会被修改,返回全新数组。
关键代码
函数先逐项校验二元数组、有限数值与 start 不大于 end,错误信息带上原始下标便于定位脏数据。随后复制到新数组并按起点、终点排序,避免依赖调用方顺序。合并阶段只维护结果数组的最后一个区间:若新区间起点不超过其终点,就把终点更新为两者较大值;否则追加新区间。排序保证起点单调,因此不需要回看更早的区间。
使用注意
区间按闭区间处理,[1,2] 与 [3,4] 不会合并;若业务语义是半开区间,需要把判断条件从小于等于改成小于。函数不合并整数相邻但不重叠的情况,例如 [1,2] 与 [3,4] 之间缺少整数 3 是否应合并需由业务决定。浮点数比较直接使用数值大小,极端精度场景应改用整数刻度。空数组返回空数组。
验证方式
测试重叠、包含、恰好相接与完全分离四类区间,断言输出数量与边界;打乱输入顺序后验证结果一致;检查原区间数组未被修改;传入起点大于终点、NaN、无穷大与长度不为二的元素,分别断言 RangeError 或 TypeError;空数组返回空数组。
代码示例
export function mergeIntervals(intervals) {
if (!Array.isArray(intervals)) throw new TypeError("intervals must be an array");
const normalized = intervals.map((interval, index) => {
if (!Array.isArray(interval) || interval.length !== 2) {
throw new TypeError("interval at index " + index + " must be [start, end]");
}
const [start, end] = interval;
if (!Number.isFinite(start) || !Number.isFinite(end)) {
throw new TypeError("interval bounds must be finite numbers");
}
if (start > end) {
throw new RangeError("interval start must not exceed end at index " + index);
}
return [start, end];
});
normalized.sort((left, right) => left[0] - right[0] || left[1] - right[1]);
const merged = [];
for (const [start, end] of normalized) {
const last = merged[merged.length - 1];
if (last && start <= last[1]) {
last[1] = Math.max(last[1], end);
} else {
merged.push([start, end]);
}
}
return merged;
}