← 中文资源

简体中文版本

合并重叠与相邻区间

校验并排序闭区间,合并重叠或恰好首尾相接的范围,同时保持输入数组不变。

阅读英文原文

功能说明

该示例接收若干闭区间并输出不重叠的最小覆盖集合。两个区间只要满足后一个起点小于等于前一个终点就合并,因此恰好相邻的 [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;
}