题干与适用场景
给定两个数组 A 与 B。每个元素是闭区间 [start, end],两个数组都依 start 非递减排序,且同一数组内没有重叠区间。返回所有同时落在 A 与 B 的区间,结果也要按起点排序。
例如 A = [[1,5],[10,14]]、B = [[2,3],[4,12]],交集是 [[2,3],[4,5],[10,12]]。端点相等算有交集;因此 [1,2] 与 [2,4] 的交集是 [2,2]。
面试官考察点
面试官要看你能否把「两个已排序序列」转成双指针扫描,而不是把所有区间两两比较。强回答会先定义闭区间语义,再用 max(start) 与 min(end) 计算当前交集,最后说明为什么只前进结束较早的那一边。
回答前需要澄清的问题
- 区间是闭区间还是半开区间?这会改变端点相等时是否输出。
- 每组是否保证已排序且组内不重叠?若不保证,需先排序,或先合并每组区间。
- 输入是否可能为空、包含单点区间或
start > end?这决定验证与错误处理。 - 是否要求保留重叠长度为零的单点?本题闭区间要求保留。
30 秒回答框架
「我用两个指针 i、j 从两组第一个区间开始。当前交集左端是两个起点的较大值,右端是两个终点的较小值;只要左端不大于右端就输出。接着移动终点较小的区间,因为它已不可能再和另一个当前或更后面的区间产生新的交集。每个指针最多走完整个数组,所以时间是 O(m+n),额外空间不含输出为 O(1)。」
分步骤深入解答
第一步:固定区间语义
将每个区间视为 [start,end]。若 left = max(A[i].start, B[j].start)、right = min(A[i].end, B[j].end),左端不大于右端就代表存在交集,两端相等是合法单点。
第二步:推导指针移动
若 A[i].end 小于 B[j].end,A[i] 在时间轴上先结束。由于 B[j] 之后的区间起点不会更早,A[i] 不可能再与 B[j+1] 或更后区间重叠,因此只能 i++。B[j].end 小于 A[i].end 时对称地 j++。
第三步:处理同时结束
若两个终点相等,两边都已经没有剩余范围可和下一个区间重叠,必须同时递增 i、j。只递增一边会再次拿已结束的区间比较,可能漏掉下一段或增加无效步骤。
第四步:写出可执行骨架
function intersect(A: number[][], B: number[][]): number[][] {
const out: number[][] = [];
let i = 0;
let j = 0;
while (i < A.length && j < B.length) {
const left = Math.max(A[i][0], B[j][0]);
const right = Math.min(A[i][1], B[j][1]);
if (left <= right) out.push([left, right]);
if (A[i][1] < B[j][1]) i++;
else if (B[j][1] < A[i][1]) j++;
else { i++; j++; }
}
return out;
}第五步:用不变量说明正确性
每次回圈开始时,i、j 指向尚未证明不可能产生交集的最早区间。计算出的 [left,right] 是这两个区间唯一可能的交集;若有交集就输出。移动较早结束的一侧后,所有被跳过的配对都因时间顺序不可能相交,因此不会漏解。
第六步:分析复杂度与输入防御
两个指针只会前进,最多 m+n 次,时间复杂度 O(m+n)。输出数组不计入额外工作空间时是 O(1);若计入输出,空间是 O(k)。若契约不保证排序或有效端点,应先验证或排序,不能直接套用线性解法。
高质量示例回答
我先确认区间是闭区间、两组都已按起点排序且组内不重叠。扫描 A[i] 与 B[j] 时,交集就是起点取大、终点取小;在闭区间下左端不大于右端都要输出。之后我移动终点较小的指针,因为它已经结束,后面的区间起点只会更晚,不可能补回新的交集;两个终点相等则同时移动。这样每个区间只被处理一次,时间 O(m+n)。我会测空数组、没有交集、端点相等、单点区间、完全包含与多段连续交集。
常见错误
- 错误表现 → 只在左端小于右端时输出 → 失败原因:闭区间的单点交集被漏掉 → 修正方法:先确认契约,左端不大于右端时也要输出。
- 错误表现 → 每个
A区间扫过所有B区间 → 失败原因:忽略排序,时间退化为O(mn)→ 修正方法:维护两个单调指针。 - 错误表现 → 永远只递增
i→ 失败原因:B[j]可能先结束,导致重复比较或漏解 → 修正方法:比较两个终点,较小者前进,同值时两者前进。 - 错误表现 → 输入未排序仍宣称线性 → 失败原因:指针移动的证明失效 → 修正方法:补上排序或每组先合并的前置步骤。
追问及应对
如果区间改成半开区间 [start,end),哪些条件要改?
交集存在的条件改成左端小于右端;[1,2) 与 [2,4) 不产生单点交集。指针移动的终点比较仍可沿用,但要在答案中明确说明端点语义。
如果每组区间没有排序且可能重叠,还能做到 O(m+n) 吗?
不能直接做到。先各自按起点排序,并合并组内重叠区间,再做双指针;成本至少包含排序的 O(m log m+n log n),之后扫描仍是线性。
如果要返回交集长度总和,而不是区间列表呢?
仍用相同扫描;每次有交集就累加 right-left 加上端点是否包含的修正。对闭区间的整数时间轴,需先定义长度是几何长度还是点数,不能直接混用。
如果输入是两个数据流,不能回看已读区间呢?
只要每个数据流仍按起点排序,双指针状态可以保留当前区间与下一次读取位置。输出一段交集后丢弃已结束的一侧;若要处理回溯或乱序,就需要缓冲与不同设计。