我有两个元组列表,每个元组都由开始时间和结束时间(从纪元开始的秒数)组成,如下所示:
list1= [(2,4), (7,10), (14,22)]
list2 = [(1,3), (5,8), (9,15), (20,24)]
我需要创建一个新的元组列表(开始,结束),以删除list2
中的元组与list1
中的元组重叠的时间间隔。
在给定list1
和list2
的情况下,该方法的预期输出为:
[(3,4), (8,10), (15,20)]
对于价值而言,list1
和list2
都将包含唯一的时间范围,即每个单独的列表内都不会重叠。
答案 0 :(得分:2)
这是一个仅在列表的每个元素上迭代一次的解决方案。
有了list1 = [(2, 4), ...]
和list2 = [(1, 3), ...]
,我们有了:
输出由我们都位于区间内而不是被删除的部分组成。
因此,我们的想法是按顺序浏览事件,跟踪我们是否处于间隔内以及是否位于已删除的部分内。
我们首先从每个列表创建一个生成器,该生成器将生成:
Event(pos=2, toggle='in_interval')
,Event(pos=4, toggle='in_interval')
,依此类推。 Event(pos=1, toggle='in_deleted')
,Event(pos=3, toggle='in_deleted')
... 我们不必关心每个值是间隔的开始还是结束,因为它只是切换状态(间隔的内部/外部)。
然后我们可以使用heapq.merge从这两个生成器中按顺序获取事件,这将为我们提供类似的信息:
Event(pos=1, toggle='in_deleted')
,Event(pos=2, toggle='in_interval')
,Event(pos=3, toggle='in_deleted')
,Event(pos=4, toggle='in_interval')
...
每个事件将切换关联的状态。当我们既处于间隔中又不在删除的部分中时,我们将为输出创建一个新的间隔。其余的不言而喻...
from heapq import merge
from itertools import chain
from collections import namedtuple
def remaining(intervals, deleted):
Event = namedtuple('Event', ['position', 'toggle'])
int_iter = (Event(position=pos, toggle='in_interval') for pos in chain.from_iterable(intervals))
del_iter = (Event(position=pos, toggle='in_deleted') for pos in chain.from_iterable(deleted))
state = {'in_interval': False, 'in_deleted': False}
start = None
out = []
for event in merge(int_iter, del_iter):
state[event.toggle] = not state[event.toggle]
if state['in_interval'] and not state['in_deleted']:
# start a new interval
start = event.position
elif start is not None:
# end an interval. If it's not empty, we append it to the output
if event.position > start:
out.append((start, event.position))
start = None
return out
示例输出:
list1 = [(2,4), (7,10), (14,22)]
list2 = [(1,3), (5,8), (9,15), (20,24)]
print(remaining(list1, list2))
# [(3, 4), (8, 9), (15, 20)]