所以我有一个包含间隔端点的集合。例如,
Set s = {(1,4),(3,7),(5,8),(14,17),(0,2),(11,14)}
我需要一种方法来找出有多少重叠间隔。在上面的例子中,答案是5,因为
(1,4) --> (3,7), (0,2)
(3,7) --> (5,8),(0,2)
(5,8) -->
(14,17) --> (11,14)
我需要一种算法,需要O(N log N)
时间来找出总和。现在,如果我对所有起点进行排序并在每个点Find number range intersection上应用此处建议的答案,我会得到一个O(N ^ 2)解。除了集合之外我还需要什么样的数据结构?谢谢!
答案 0 :(得分:4)
为每个区间[a,b]构建值列表(a,-1),(b,1)。现在对这些进行排序可让您遍历数组,计算每个端点当前打开的间隔数,只需聚合+1和-1即可。
重要的是(b,1)在排序列表中位于(b,-1)之后;因为即使交叉点在一个点上,我们也想计算交点。
此处填写完整的代码。
def overlaps(intervals):
es = []
for a, b in intervals:
es.append((a, -1))
es.append((b, 1))
es.sort()
result = 0
n = 0
for a, s in es:
if s == -1: result += n
n -= s
return result
注意,这通常是O(n log n),并且不需要任何花哨的数据结构。
答案 1 :(得分:2)
首先,我假设您的示例(0,1)
和(1,2)
重叠。
顺便说一句,您的示例不正确,(3,7)
与(0,2)
不重叠
解决此问题的一种方法是:
第1步可以在O(n log n)
中完成
步骤2在进行计数时迭代所有间隔。所以它是O(n * X)
,其中X
是进行计数的复杂性。使用Fenwick Tree,我们可以在O(log n)
1 中执行此操作,因此步骤2也可以在O(n log n)
中完成。
因此整体复杂度为O(n log n)
。
伪代码:
def fenwick_tree.get(num):
return the sum from counter[0] to counter[num]
def fenwick_tree.add(num, val):
add one to counter[num]
intervals = [...]
sort(intervals) # using the starting point as the key
init_fenwick_tree()
sum = 0
count = 0
for (starting_point, end_point) in intervals:
sum = sum + (count - fenwick_tree.get(starting_point-1))
fenwick_tree.add(end_point,1)
return sum
Python代码(仅在间隔点为非负整数时才有效):
MAX_VALUE = 2**20-1
f_arr = [0]*MAX_VALUE
def reset():
global f_arr, MAX_VALUE
f_arr[:] = [0]*MAX_VALUE
def update(idx,val):
global f_arr
while idx<MAX_VALUE:
f_arr[idx]+=val
idx += (idx & -idx)
def read(idx):
global f_arr
if idx <= 0:
return 0
result = 0
while idx > 0:
result += f_arr[idx]
idx -= (idx & -idx)
return result
intervals = [(1,4),(3,7),(5,8),(14,17),(0,2),(11,14)]
intervals = sorted(intervals, key=lambda x: x[0])
reset()
total = 0
for processed, interval in enumerate(intervals):
(start, end) = interval
total += processed - read(start-1)
update(end, 1)
print total
将打印来自这些重叠的4
:
(0,2) - (1,4) (1,4) - (3,7) (3,7) - (5,8) (11,14) - (14,17)
请注意,我们无法获得重叠间隔,因为在最坏的情况下会出现O(n^2)
重叠,无法在O(n log n)
时间内打印。
1 实际上,Fenwick树在O(log M)
时间内进行计数,其中M
是区间中最大可能的不同值。请注意M <= 2n
,因为只有很多不同的值。因此,说Fenwick Tree在O(log n)
时间进行计数也是正确的。
答案 2 :(得分:1)
快速提示:首先对间隔进行排序。现在查看您的间隔,将每个添加到端点排序的最小堆。对于每个间隔,从堆中删除小于该间隔开始点的所有内容。堆中剩余的每个端点表示在此间隔之前开始并且与其重叠的间隔,因此按照堆的大小递增overlaps
。现在将当前间隔添加到堆中并继续。
在伪代码中:
Sort(intervals)
(firstStart, firstEnd) = intervals[0]
currentIntervals = MinHeap()
overlaps = 0
for each (start, end) in intervals[1:]:
remove from currentIntervals all numbers < start
overlaps += Size(currentIntervals)
HeapPush(end, currentIntervals)
return overlaps
我没有测试过这个,但似乎至少看似合理。
答案 3 :(得分:0)
这可以简单地使用贪婪技术来完成。 只需按照以下步骤操作:
根据起点对间隔进行排序 从最低起点到最高点迭代 计算大于或等于当前起始点的先前端点的数量。 增加当前终点的计数。
答案 4 :(得分:0)
保罗的回答非常聪明。如果是采访,我认为我不能提出这个想法。这里我有另一个版本,也是O(nlog(n))。
import heapq
def countIntervals(s):
s.sort()
end = [s[0][1]]
res, cur = 0, 0
for l in s[1:]:
if l[0]>heapq.nsmallest(1, end)[0]:
heapq.heappop(end)
cur = len(end)
res += cur
end.append(l[1])
return res
我们维护一个存储即将到来的端点的最小堆。每当新的间隔进入时,我们应该将它的起点与最小的终点进行比较。
如果起点较大,则表示最小的终点(表示该间隔)永远不会导致更多重叠。因此我们把它弹出来。
如果起点较小,则表示所有终点(相应的间隔)与新的间隔重叠。
然后,终点数(“cur”)是这个新的未来区间带来的重叠数。在我们更新结果之后,我们将新的即将到来的间隔的端点添加到堆中。