区间合并简介
区间合并问题是指在给定多个区间的情况下,找出能够合并的区间,使得最终的结果区间尽量少,并且没有重叠。这个问题通常出现在处理一系列任务或事件的时间段、区间操作、或者连续的数字范围问题中。
例如,给定一些时间段,你需要合并所有相交或相邻的时间段,使得最终的结果更加简洁。
区间合并的基本思路
-
排序:
- 首先,按照区间的起始位置进行排序。排序的目的是为了确保所有的重叠区间可以在顺序上被依次处理。
-
合并:
- 初始化一个结果列表,遍历每个区间。
- 对于当前区间,如果它与结果列表中的最后一个区间重叠(即起始点小于等于结果区间的结束点),则合并这两个区间;否则,将当前区间加入结果列表。
-
合并条件:
- 区间
[a, b]
和[c, d]
可以合并当且仅当b >= c
,即第一个区间的结束位置不大于第二个区间的开始位置。
- 区间
区间合并的解题步骤
- 排序:按照每个区间的起始点对区间进行排序。
- 合并:遍历排序后的区间,逐一检查是否能合并到结果区间中。
- 如果可以合并,更新结果区间的结束位置。
- 如果不能合并,将当前区间添加到结果区间中。
区间合并经典例题
例题1:合并区间
题目描述: 给定一个区间的集合 intervals
,请合并所有重叠的区间。返回合并后的区间。
解法:
- 对所有区间按起始点进行排序。
- 使用一个空的结果列表,遍历排序后的区间。
- 如果当前区间和结果中的最后一个区间重叠,合并它们;否则,直接将当前区间加入结果列表。
代码实现:
def merge_intervals(intervals):
# Step 1: 按照区间的起始位置进行排序
intervals.sort(key=lambda x: x[0])
merged = []
for interval in intervals:
# 如果 merged 为空或当前区间与 merged 中最后一个区间不重叠,则直接添加
if not merged or merged[-1][1] < interval[0]:
merged.append(interval)
else:
# 如果当前区间与 merged 中最后一个区间重叠,合并区间
merged[-1][1] = max(merged[-1][1], interval[1])
return merged
# 示例
intervals = [[1, 3], [2, 6], [8, 10], [15, 18]]
print(merge_intervals(intervals))
# 输出: [[1, 6], [8, 10], [15, 18]]
时间复杂度:O(nlogn)O(n \log n),其中 nn 是区间的数量。排序的时间复杂度是 O(nlogn)O(n \log n),遍历区间的时间复杂度是 O(n)O(n)。
例题2:插入区间
题目描述: 给定一个区间列表 intervals
和一个新的区间 newInterval
,请将新的区间插入到区间列表中,确保合并后的区间没有重叠。
解法:
- 将
newInterval
插入到适当的位置。 - 然后和已有的区间一起进行合并。
代码实现:
def insert_interval(intervals, new_interval):
# Step 1: 插入新区间并排序
intervals.append(new_interval)
intervals.sort(key=lambda x: x[0])
merged = []
for interval in intervals:
# 如果 merged 为空或当前区间与 merged 中最后一个区间不重叠,则直接添加
if not merged or merged[-1][1] < interval[0]:
merged.append(interval)
else:
# 如果当前区间与 merged 中最后一个区间重叠,合并区间
merged[-1][1] = max(merged[-1][1], interval[1])
return merged
# 示例
intervals = [[1, 3], [6, 9]]
new_interval = [2, 5]
print(insert_interval(intervals, new_interval))
# 输出: [[1, 5], [6, 9]]
时间复杂度:O(nlogn)O(n \log n),其中 nn 是区间的数量。插入新区间并排序的时间复杂度是 O(nlogn)O(n \log n),合并区间的时间复杂度是 O(n)O(n)。
例题3:区间的非重叠部分
题目描述: 给定一组区间,请找到区间中不重叠的部分的总长度。即对于每一对区间,计算它们的非重叠部分。
解法:
- 对区间进行合并。
- 合并后的区间的总长度就是合并后的区间的所有长度的和。
代码实现:
def non_overlapping_length(intervals):
# Step 1: 对区间进行排序
intervals.sort(key=lambda x: x[0])
merged = []
total_length = 0
for interval in intervals:
# 如果 merged 为空或当前区间与 merged 中最后一个区间不重叠,则直接添加
if not merged or merged[-1][1] < interval[0]:
merged.append(interval)
total_length += interval[1] - interval[0]
else:
# 如果当前区间与 merged 中最后一个区间重叠,合并区间
total_length -= merged[-1][1] - merged[-1][0] # 减去原先的长度
merged[-1][1] = max(merged[-1][1], interval[1]) # 合并区间
total_length += merged[-1][1] - merged[-1][0] # 加上合并后的长度
return total_length
# 示例
intervals = [[1, 3], [2, 6], [8, 10], [15, 18]]
print(non_overlapping_length(intervals))
# 输出: 8
时间复杂度:O(nlogn)O(n \log n),其中 nn 是区间的数量。排序的时间复杂度是 O(nlogn)O(n \log n),遍历区间的时间复杂度是 O(n)O(n)。
总结:区间合并技巧
应用场景 | 关键步骤 | 描述 |
---|---|---|
合并区间 | 排序 -> 合并 | 对区间进行排序并合并重叠区间 |
插入新区间 | 插入新区间 -> 排序 -> 合并 | 将新区间插入并进行合并 |
非重叠部分计算 | 合并区间 -> 计算总长度 | 对所有区间进行合并后计算非重叠部分的总长度 |
动态合并区间 | 动态更新区间 -> 合并 | 对实时更新的区间进行合并操作 |
区间合并是一个典型的排序和合并问题,最重要的是根据区间的起始位置进行排序,再根据合并的规则逐步合并区间。通过这种方式,可以高效地解决很多区间类问题。