我有一大堆具有起始编号和结束编号的对象。例如:
(999, 2333, data)
(0, 128, data)
(235, 865, data)
...
假设间隔彼此不重叠。我正在编写一个函数,它接受一个数字并找到(低,高)包含它的对象。说给出333,我想要列表中的第3个对象。
有没有什么方法可以尽可能有效地完成这项工作,缺少线性搜索?我在考虑二元搜索,但在处理范围检查方面遇到了一些困难。
答案 0 :(得分:8)
想想是否值得对数据进行排序。
如果您只想搜索几次,那么它就不会 - 而且您无法避免线性搜索。搜索的总体复杂程度为O(n*k)
,其中n
是元素数量,k
是搜索次数。
如果您想要搜索很多次,那么您应首先排序,然后使用二进制搜索进行搜索。它将O(nlogn)
用于排序,O(klogn)
用于搜索k次,因此总共获得O((n+k)logn)
。
因此,只有k>=logn
P.S。您可以使用其他方法进行排序和搜索,如其他答案中所提出的,在所有方面,结论仍然是:仅在k>=logn
答案 1 :(得分:2)
您可以使用bisect模块:http://docs.python.org/library/bisect.html
您需要对数据进行一次排序,然后使用bisect:
import bisect
data=None
tuples=[(0, 128, None), (235, 865, None), (999, 2333, None)]
tuples.sort()
print tuples
print bisect.bisect(tuples, (-1,)) # 0
print bisect.bisect(tuples, (1,)) # 1
print bisect.bisect(tuples, (333,)) # 2
print bisect.bisect(tuples, (3333,)) # 3
答案 2 :(得分:2)
如果搜索速度至关重要,那么您可以创建一个查找表(已经由S.Lott评论过)。这将采用 O ( r )内存(其中 r 是范围的大小), O (< em> r )预处理时间, O (1)搜索时间。创建一个范围大小的数组,并使用指向数据的指针填充每个元素,或者为null。
lookup = {}
for low, high, data in source_ranges:
for i in range(low,high): # or maybe high+1 if the ranges are inclusive
lookup[i] = data
现在查找很简单。
答案 3 :(得分:1)
首先,完全不清楚二进制搜索是否合理。当间隔数量很小时,线性搜索可能会更快。
如果您担心性能问题,那么谨慎的做法是对代码进行分析,并对两种方法进行基准测试。
免责声明,二进制搜索可以通过对间隔进行一次排序,然后重复使用bisect
模块进行搜索来实现:
import bisect
intervals = [(999, 2333, 'int1'), (0, 128, 'int2'), (235, 865, 'int3')]
intervals.sort()
def find_int(intervals, val):
pos = bisect.bisect_left([interval[1] for interval in intervals], val)
if pos < len(intervals) and val >= intervals[pos][0]:
return intervals[pos]
else:
return None
print(find_int(intervals, 0))
print(find_int(intervals, 1))
print(find_int(intervals, 200))
print(find_int(intervals, 998))
print(find_int(intervals, 999))
print(find_int(intervals, 1000))
print(find_int(intervals, 2333))
print(find_int(intervals, 2334))
在上文中,我假设间隔不重叠,并且间隔包括其起点和终点。
最后,为了提高性能,可以考虑将[interval[1] for interval in intervals]
从函数中分解出来并在开始时只执行一次。