有效地确定后续数字范围中的数字是否在有序列表中。 (在Python中)

时间:2014-07-18 09:30:32

标签: python list

我们有一个有序的数字列表。现在我们要检查一系列数字的成员是否在列表中。

range(5, 10) in mylist之类的东西 如果5,6,7,8或9位于mylist中,则应返回列表中首次找到的元素,否则为Null或False。

例如,如果mylist类似于[1,2,3,7,8,10,15],则函数将返回7.如果列表为[1,2,3,4,12,13],则函数将返回None / False。

现在想想大名单和大范围,操作变得无法实现。我该如何实现它,以便它有更好的性能?

1 个答案:

答案 0 :(得分:5)

对于您的范围的每个边界,您可以binary-search两次(使用bisect.bisect_left())。

如果返回的索引相同,则没有交集(返回None)。

如果不是,请返回start_index处的元素(其中start_index是您为范围start获得的索引)。

以下是代码:

import bisect
def intersect_range(lst, start, stop):
     start_i = bisect.bisect_left(lst, start)
     stop_i = bisect.bisect_left(lst, stop)
     if start_i == stop_i:
         return None
     else:
         return lst[start_i]

intersect_range([1,2,3,7,8,10,15], 5, 10)
=> 7
intersect_range([1,2,3,7,8,10,15], 5, 6)
=> None
intersect_range([1,2,3,7,8,10,15], 15,30)
=> 15
intersect_range([1,2,3,7,8,10,15], 0,1) # "stop" is excluded from range
=> None

由于您执行了两次二进制搜索,因此复杂度为O(logN),其中N是列表的长度。


编辑:

还有一个稍快的替代方案,即二进制搜索范围的开始,然后检查lst[start_index]是否在范围内(start <= lst[start_i] < stop)。这将logN操作的数量从两个减少到一个。代码如下所示:

def intersect_range(lst, start, stop):
    start_i = bisect.bisect_left(lst, start)
    if start <= lst[start_i] < stop:
        return lst[start_i]
    else:
        return None