区间合并问题是一类十分经典的贪心问题 , 在豆包MarsCode AI刷题过程中我也有了新的感悟和总结 , 所以总结了一个通用一点的模板 ,并附上两道例题
区间合并的模板总结
先给大家贴一个通用的模板
from typing import List, Tuple
def merge(segs: List[Tuple[int, int]]) -> List[Tuple[int, int]]:
segs.sort()
res = []
st, ed = -2e9, -2e9
for seg in segs:
if ed < seg[0]:
if st != -2e9:
res.append((st, ed))
st, ed = seg
else:
ed = max(ed, seg[1])
if st != -2e9:
res.append((st, ed))
return res
区间合并问题是一个典型的贪心算法问题,常出现在面试和竞赛中。以下以你的代码为例,总结区间合并问题的解题思路模板,并进行详细解析。
解题思路模板
-
排序:保证处理顺序
- 按区间的左端点(
start)升序排序。 - 如果左端点相同,可以按右端点(
end)排序,但通常不需要。
- 按区间的左端点(
-
初始化变量:记录当前合并区间
- 使用变量
st和ed分别记录当前区间的起点和终点,初始值可以设为无效值(如极小值)。
- 使用变量
-
遍历区间:判断是否重叠
-
如果当前区间的起点大于当前记录区间的终点(
st, ed),说明没有重叠:- 将当前记录的区间加入结果集。
- 更新
st和ed为当前区间的起点和终点。
-
如果当前区间的起点小于等于记录区间的终点,说明有重叠:
- 更新
ed为当前记录的区间终点和当前区间终点的较大值,完成合并。
- 更新
-
-
处理最后一个区间
- 遍历完成后,需要将最后一个区间加入结果集。
-
返回结果集
- 结果集即为合并后的区间列表。
我们基于这个总结的模板 ,只需要按照题意稍加改造代入
例题
1.唐门绝技:暴雨梨花针最少发射次数 - MarsCode
题目解析 :
唐三需要利用垂直发射的 暴雨梨花针,覆盖所有靶子(二维平面上的线段)。 这看似是一个二维的题 , 由于暴雨梨花针向上发射并覆盖 y = 100 范围。但我们只需要稍加思考就知道(我记得之前给的数据是y最大是100) , 我们只需要考虑x轴怎么丢针才能覆盖最多的区间 , 好的 ,成功转化为区间问题 , 套模板 , 代码如下
def solution(k, p, target):
segs = [(i[0], i[1]) for i in target]
segs.sort()
st, ed = -1, -1
res = 0
for seg in segs:
if ed < seg[0]:
res += 1
st, ed = seg[0], seg[1]
else:
st = max(seg[0], st)
ed = min(seg[1], ed)
return res % p
2. 打点计数器的区间合并 - MarsCode
题目解析 :
这题思路就更明显了,一眼就知道可以套模板 ,计数器的目标是接收多个 数字范围(区间) ,将这些范围中的所有数字合并,并对 每个唯一数字 打点。
步骤
- 区间合并问题:将多个可能重叠的区间合并,得到最终的不相交区间。 直接使用区间合并模板
- 计数问题:计算合并后的所有区间内的数字总数。
def solution(input_array):
segs = [(x[0], x[1]) for x in input_array]
segs.sort()
res = 0
st, ed = -2e9, -2e9
for seg in segs:
if ed < seg[0] - 1:
if st != -2e9:
res += ed - st + 1
st, ed = seg[0], seg[1]
else:
ed = max(ed, seg[1])
if st != -2e9:
res += ed - st + 1
return res
if __name__ == "__main__":
# Test cases
test_array1 = [[1, 4], [7, 10], [3, 5]]
test_array2 = [[1, 2], [6, 10], [11, 15]]
print(solution(test_array1) == 9) # Expect true
print(solution(test_array2) == 9) # Expect true