Frontend SWE / Supply Chain · Phone Coding

Apple 前端 Merge Intervals:端点、排序与扫描不变量

从公开题名还原可证明的闭区间训练版,说明相接端点与半开时间区间的差异。

题目重点:从公开题名还原可证明的闭区间训练版,说明相接端点与半开时间区间的差异。

题意与假设

这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。

  • 训练版输入为合法闭区间 `[start,end]`,起点≤终点;共享端点属于重叠,例如 [1,2] 和 [2,3] 合为 [1,3]。
  • 返回按 start 升序排列的最少个闭区间并集,不修改原输入;若业务是半开预约 `[start,end)` 且仅合并真重叠,等号处须改规则。

解题路线

  1. 按 `(start,end)` 排序。维护 merged 最后一段,它是已扫描区间形成的最后一个连通并集分量。
  2. 若下一段 start>最后段 end,它与已处理的最后分量不相交,开启新分量;否则把 end 扩到两者最大值。
  3. 正确性来自排序:后续 start 只会不小于当前 start,若已经与最后分量分离,就不可能再和更早分量重叠;若相交,扩展覆盖不丢点。
  4. 对 41,371 组短区间序列,以独立的重叠图连通分量闭包计算并集对照。

手动走一遍

[1,3]、[2,4]、[8,9]、[9,10] 经排序后,前两段合为 [1,4],后两段共享端点 9 合为 [8,10]。若第二组是半开区间 [8,9)、[9,10),它们没有共同时间点,但有些 API 仍选择‘相邻可合并’;须确认语义。

Python 参考实现

下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。

代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。

def merge_closed_intervals(intervals: list[tuple[int, int]]) -> list[tuple[int, int]]:
    """Union closed intervals; endpoint equality counts as overlap."""
    if any(start > end for start, end in intervals):
        raise ValueError("interval start must not exceed end")
    ordered = sorted(intervals)
    merged: list[tuple[int, int]] = []
    for start, end in ordered:
        if not merged or start > merged[-1][1]:
            merged.append((start, end))
        else:
            previous_start, previous_end = merged[-1]
            merged[-1] = (previous_start, max(previous_end, end))
    return merged

复杂度

排序 O(n log n),扫描 O(n),新输出 O(n) 空间;Python `sorted` 另用 O(n) 列表。若输入已按起点排序,可 O(n) 时间完成。

易错点与追问

容易失分的边界

  • 端点开闭未确认就把 `start==end` 固定当重叠。
  • 只与原始上一段比较,不用合并后的最大 end,漏掉链式覆盖。
  • 排序只按 end 导致扫描不变量失效。
  • 零长度区间与非法 `start>end` 不处理。
  • 把此标准训练版宣称为题目精确输入格式。

继续深挖

  • 如果是半开预约且只合并真实重叠,判断条件怎么改?
  • 如何在线接收新区间并维持并集?
  • 如何输出总覆盖长度而不逐点枚举?
  • 怎样处理带时区的时间区间?
✓已复制微信号!