题目:
小M的程序设计大作业是编写一个多任务下载器。在实现过程中,他遇到了一个问题:在一次下载过程中,总共有N个任务,每个任务会在第x秒开始,并持续y秒。小M需要知道,在同一时刻,最多有多少个任务正在同时下载,也就是计算出任务的最高并发数。 ·n 表示任务的数量。 array 是一个二维列表,每个元素为[x,y],表示任务的开始时间和持续时间,其中: ·x表示任务的开始时间; y 表示任务的持续时间。
要解决这个问题,我们需要计算在任意时刻,最多有多少个任务正在同时下载。具体来说,每个任务有开始时间 x 和持续时间 y,任务在时间区间 [x, x + y) 内会持续下载。我们的目标是找出最大同时下载任务数。
思路
我们可以将任务的“开始”事件和“结束”事件分别处理,使用一种事件驱动的方式来模拟任务的并发数。
-
事件类型:
- 对于每个任务
[x, y],我们可以将其分为两个事件:开始事件,表示任务在时间x开始。结束事件,表示任务在时间x + y结束。
- 对于每个任务
-
处理事件:
- 将所有任务的开始时间和结束时间作为事件记录下来。
- 对事件按时间排序。如果时间相同,结束事件优先于开始事件(防止某个任务刚好在另一个任务结束时开始,导致重复计数)。
-
模拟并发数:
- 遍历这些排序后的事件,维护当前并发数,并记录最大并发数。
具体步骤
-
创建事件列表: 对于每个任务
[x, y],生成两个事件:一个是(x, +1)(表示开始任务),一个是(x + y, -1)(表示结束任务)。 -
排序事件: 先按时间排序;如果时间相同,则结束事件 (
-1) 排在开始事件 (+1) 前面。 -
遍历事件: 遍历排序后的事件,更新当前并发数,并记录最大并发数。
代码实现
def solution(n, array):
events = []
# 构造所有的事件
for task in array:
start, duration = task
events.append((start, 1)) # 任务开始,+1
events.append((start + duration, -1)) # 任务结束,-1
# 按时间排序,时间相同则结束事件排在开始事件前
events.sort(key=lambda x: (x[0], x[1]))
max_concurrent = 0
current_concurrent = 0
# 遍历事件,更新并发数
for event in events:
current_concurrent += event[1] # 事件 +1 或 -1
max_concurrent = max(max_concurrent, current_concurrent)
return max_concurrent
# 测试用例
if __name__ == "__main__":
print(solution(2, [[1, 2], [2, 3]]) == 2) # 两个任务完全重叠
print(solution(4, [[1, 2], [2, 3], [3, 5], [4, 3]]) == 3) # 在时间 2 和 3 时,有 3 个任务同时下载
解释
- 事件列表的构建:
- 每个任务
[x, y]被拆成两个事件:(x, +1)表示开始下载,(x + y, -1)表示结束下载。
- 每个任务
- 事件排序:
- 事件按时间排序,如果时间相同,结束事件
(-1)会排在开始事件(+1)前面,确保我们不会重复计数任务。
- 事件按时间排序,如果时间相同,结束事件
- 遍历事件:
current_concurrent记录当前时刻的并发任务数,每遇到一个+1事件就增加并发数,每遇到一个-1事件就减少并发数。max_concurrent记录并发数的最大值。
复杂度分析
- 时间复杂度:排序事件的时间复杂度是
O(n log n),其中n是任务数量。遍历事件的时间复杂度是O(n)。因此,总的时间复杂度为O(n log n)。 - 空间复杂度:我们使用了一个事件列表来存储所有任务的开始和结束事件,空间复杂度是
O(n)。
测试
-
测试1:
solution(2, [[1, 2], [2, 3]]) == 2- 在时间
[1, 2)和[2, 5)这两个时间段内都有任务下载,最大并发数为 2。
- 在时间
-
测试2:
solution(4, [[1, 2], [2, 3], [3, 5], [4, 3]]) == 3- 在时间
[2, 3)和[3, 5)时,共有 3 个任务同时下载。
- 在时间
通过这种方法,我们能够高效地计算出最大并发数。
以上为本题的解题思路。