技术岗位 · Coding

恰好一人在线的时间段:合并个人区间后扫事件

返回活跃人数恰为1的全部片段,不是所有有人在线的并集。

题目重点:返回活跃人数恰为1的全部片段,不是所有有人在线的并集。

题意与假设

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

  • 训练代码输入半开区间,每区间代表一个不同人的一次活动;若同一人有多个区间,先按人合并,避免同一人自重叠计两人。
  • 零长区间不占用,端点a≤b;输出相接且同满足“人数1”的片段可合并,即使活跃身份发生变化。

解题路线

  1. 每起点+1,终点−1,聚合同一时刻事件。排序后,处理时刻t的事件前,active适用于[last,t)。
  2. 当active=1输出该片段,再应用t的总delta。这样相同结束/开始边界不会产生瞬时双人。
  3. 如果需要返回“谁唯一在线”,另维护活跃身份集合,并且不同身份的相接片段不能合并。当前计数结果不含身份。

手动走一遍

A[1,4),B[3,6):恰一人为[1,3)和[4,6),[3,4)两人不输出。A[1,2),B[2,3):人数全程1,可合并[1,3);若要身份则分两段。

Python 参考实现

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

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

def covered_once(intervals):
    events = {}
    for a, b in intervals:
        if a > b:
            raise ValueError("reversed interval")
        if a != b:
            events[a] = events.get(a, 0) + 1
            events[b] = events.get(b, 0) - 1
    active, last, result = 0, None, []
    for t in sorted(events):
        if last is not None and last < t and active == 1:
            if result and result[-1][1] == last:
                result[-1] = (result[-1][0], t)
            else:
                result.append((last, t))
        active += events[t]
        last = t
    return result

复杂度

O(nlogn)时间、O(n)事件/结果空间。按人预合并成本也按该人区间数排序计。

易错点与追问

容易失分的边界

  • 闭区间的单个端点是否算时间片需另定,连续时间通常用半开。
  • 重复同人区间未经合并会把“人数”误计为“记录数”。

继续深挖

  • 返回恰K人在线的时段,扫描条件如何改?
  • 在线增删区间和多次查询要怎样维护计数分布?
✓已复制微信号!