当前:LC57 · 插入区间 · 首次出现于 Day 8 · 路径:顶栏「56天打卡」→ 点击 LC 题号 → 逐题动画

LC57 · Insert Interval · 区间

插入区间:左边留下,中间合并,右边接上

原区间已经有序、互不重叠。新区间左边那些右端还够不着它,原样抄;中间凡是左端还没越过新右端的,并成一段;其余整段接在后面。

线性扫已排序的 intervals。第一段:右端 < 新左端,直接写入。第二段:左端 ≤ 新右端,用 min/max 并进新区间。写入这段合并结果。第三段:剩下的全部写入。时间 O(n),不必再排序。

时间 O(n)空间 O(n)结论先行 · 全文约 6 节
导读

给你一组互不重叠、已按左端排序的区间,再给一个新区间。把它插进去,必要时与重叠的原区间合并,结果仍保持有序且不重叠。

主例 intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]],newInterval = [4,8]。[1,2] 完全在 4 左边;[3,5]、[6,7]、[8,10] 都和 [4,8] 相交,并成 [3,10];[12,16] 完全在 10 右边。答案 [[1,2],[3,10],[12,16]]。

先插入再当 LC56 做也能对,但输入已经有序,再排序是浪费。本文要回答:三段的分界用哪两个不等式切开。

有序输入把列表切成左、中、右

原区间互不重叠且左端有序,所以它们在数轴上从左到右排成一排。新区间 [a,b] 扔进去,原区间只可能落在三类:整段在 [a,b] 左边、和 [a,b] 有交集、整段在 [a,b] 右边。三类在数组里是连续的三段,不会交错。

整段在左:原右端 < a,连端点都碰不到新区间,原样保留。整段在右:原左端 > b,同样碰不到。其余都是重叠,包括相切。缺的不是分类,而是一次从左扫到右、按这个顺序处理,不要把中间段拆开排序。

用手走主例的第一段。[1,2] 的右端 2 < 4,属于左边,写入结果。演示第一帧。下一个 [3,5] 的右端 5 不小于 4,左边段结束。

不必再排序LC56 的排序是因为输入乱序。这里输入已经有序,三段天然连续,线性扫一遍即可。
三个阶段:左边、重叠、右边
[1,2]
[3,5]
[6,7]
[8,10]
[12,16]
[4,8]
[1,2]
[1,2] 在新区间左边 → 直接保留

中间段用 min 左、max 右收成一段

进入重叠段时,用 [a,b] 当当前合并段。只要下一个原区间的左端 ≤ b,它就还粘着当前段:a 改成 min(a, 它的左端),b 改成 max(b, 它的右端)。左端可能比 a 更小——主例 [3,5] 把左端从 4 拉到 3;右端可能比 b 更大——[8,10] 把右端从 8 推到 10。直到某个区间左端 > b,重叠段结束。

用手走主例。[3,5]:3≤8,并成 [3,8]。[6,7]:6≤8,右端 7 不比 8 大,仍是 [3,8]。演示第一帧。 [8,10]:8≤8,右端扩到 10,变成 [3,10]。演示第二帧。 [12,16]:12>10,重叠结束,写入 [3,10],再把 [12,16] 整段接上。结果 [[1,2],[3,10],[12,16]]。

新区间也可能和谁都不重叠:左边段结束之后立刻左端 > b,中间循环零次,[a,b] 原样插入。也可能插在最前或最后,第一段或第三段为空。相切要并:原左端等于 b,或原右端等于 a,都算重叠;第一段的判断是右端 < a,不是 ≤。

重叠区间逐个并入 cur
[1,2]
[3,5]
[6,7]
[8,10]
[12,16]
[4,8]
[1,2]
[3,5]、[6,7] 并入 → cur=[3,8]

Go:三阶段

solution.goGo
func insert(intervals [][]int, ni []int) [][]int {
res := [][]int{}
a, b := ni[0], ni[1]
i := 0
for i < len(intervals) && intervals[i][1] < a {
res = append(res, intervals[i]); i++
}
for i < len(intervals) && intervals[i][0] <= b {
a = min(a, intervals[i][0])
b = max(b, intervals[i][1])
i++
}
res = append(res, []int{a, b})
for i < len(intervals) { res = append(res, intervals[i]); i++ }
return res
}

1右端 < a 的整段在左,原样写入。主例只写下 [1,2]。

2左端 ≤ b 的都还粘着,同时拉左端、推右端。

3合并结果只插入一次。主例写入 [3,10]。

4下标 i 之后全是右边,整段追加。

总结

左段原样、中段 min/max 并成一条、右段接上。主例插入 [4,8] 后得到 [1,2]、[3,10]、[12,16]。

  • 左段分界是原右端 < 新左端。相切不算左边。
  • 中段分界是原左端 ≤ 新右端。左端、右端都可能被拉开。
  • 输入已有序,不要再当 LC56 排序。
同族题目
LC56合并区间LC435无重叠区间LC452用最少数量的箭引爆气球