删除一个区间后剩余区间如何计算?相交部分为什么要切成左右两段?
简化版
删除区间 [a,b] 时,遍历每个原区间 [l,r]。如果它和删除区间不相交,原样保留;如果相交,则保留左侧残段 [l,a] 中合法部分和右侧残段 [b,r] 中合法部分,注意常见题目使用半开区间 [l,r)。
详细版
区间删除的核心是分类讨论。对于半开区间 [l,r) 和删除区间 [a,b):若 r <= a 或 l >= b,说明不相交;否则相交。相交时,如果 l < a,保留 [l,a);如果 r > b,保留 [b,r)。
每个区间最多产生两个残段,时间复杂度 O(n),空间复杂度 O(n)。面试重点是端点语义:半开区间下端点相等不算重叠。
完整版教学
一、先确认端点语义
很多删除区间题使用半开区间 [start,end),表示包含 start,不包含 end。这样 [1,3) 和 [3,5) 不相交。
| 语义 | [1,3] 与 [3,5] | [1,3) 与 [3,5) |
|---|---|---|
| 闭区间 | 相交 | 不适用 |
| 半开区间 | 不适用 | 不相交 |
易错点:区间删除题先看是闭区间还是半开区间,判断不相交条件会不同。
下面按常见半开区间讲。
二、不相交时为什么原样保留
原区间 [l,r) 和删除区间 [a,b) 不相交有两种情况:
r <= a // 原区间在删除区间左边
l >= b // 原区间在删除区间右边
这时删除操作不会影响原区间,直接加入答案。
[1,2) 删除 [3,4) => [1,2)
三、相交时为什么可能产生两段
如果原区间覆盖删除区间的左右两侧,删除中间后会裂成两段。
原区间:[1,10)
删除: [3,7)
剩余:[1,3) 和 [7,10)
所以不能只修改一个端点就结束。要分别检查左残段和右残段是否存在。
四、左残段和右残段的条件
左残段存在条件:
l < a
保留 [l, a)
右残段存在条件:
r > b
保留 [b, r)
| 原区间位置 | 剩余 |
|---|---|
| 完全被删除覆盖 | 无 |
| 只左侧剩余 | [l,a) |
| 只右侧剩余 | [b,r) |
| 两侧都剩余 | 两段 |
这些条件直接来自半开区间的合法长度必须大于 0。
五、代码模板
List<List<Integer>> removeInterval(int[][] intervals, int[] toBeRemoved) {
int a = toBeRemoved[0], b = toBeRemoved[1];
List<List<Integer>> ans = new ArrayList<>();
for (int[] in : intervals) {
int l = in[0], r = in[1];
if (r <= a || l >= b) {
ans.add(Arrays.asList(l, r));
} else {
if (l < a) ans.add(Arrays.asList(l, a));
if (r > b) ans.add(Arrays.asList(b, r));
}
}
return ans;
}
如果平台要求返回 int[][],最后再把列表转换成数组即可。
六、用例子推演
intervals=[[0,2],[3,4],[5,7]],删除 [1,6):
[0,2) 与 [1,6) 相交,剩 [0,1)
[3,4) 被完全删除,剩无
[5,7) 相交,剩 [6,7)
结果是 [[0,1],[6,7]]。注意 [3,4) 没有残段,因为它完全落在删除区间内。
七、常见误区与追问
- 误区:把半开区间端点相等当重叠。
[1,3)和[3,5)不相交。 - 误区:相交时只保留一边。 删除中间时可能裂成左右两段。
- 误区:产生空区间。 只有
l<a或r>b时才保留残段。 - 追问:如果是闭区间怎么办? 残段端点要改成
[l,a-1]、[b+1,r]这类形式,取决于整数还是连续域。 - 追问:原区间需要排序吗? 若题目保证已排序不重叠,输出自然有序;否则删除逻辑仍可逐段做。
- 追问:复杂度是多少? 每个区间最多处理一次,时间
O(n)。
八、加强记忆
删除区间的核心是“没碰到就原样,碰到了就切掉中间”。半开区间下,不相交条件是 r<=a 或 l>=b;相交时左边看 l<a,右边看 r>b。最容易错的是端点语义和中间删除导致的一分为二。