如果我有一个已经排序的列表并使用 in 关键字,例如:
a = [1,2,5,6,8,9,10]
print 8 in a
我认为这应该进行顺序搜索,但是我可以通过二进制搜索来加快速度吗? 是否有一种pythonic方式在排序列表中搜索?
答案 0 :(得分:5)
标准库具有bisect
模块,该模块支持按排序顺序进行搜索。
但是,对于小型列表,我敢打赌in
运算符后面的C实现会击败bisect
。您必须使用一堆常见情况进行测量,以确定目标硬件上真正的收支平衡点...
值得注意的是,如果您可以使用无序迭代(即set
),那么您可以在O(1)
时间平均进行查找(使用in
运算符),与O(logN)
上的in
和O(N)
运算符的序列的二分相比,lambda
。而且,通过一组,您还可以避免首先对其进行排序的成本: - )。
答案 1 :(得分:3)
标准库中的模块bisect
中存在Python的二进制搜索。它不支持in
/ contains
,但您可以编写一个小函数来处理它:
from bisect import bisect_left
def contains(a, x):
"""returns true if sorted sequence `a` contains `x`"""
i = bisect_left(a, x)
return i != len(a) and a[i] == x
然后
>>> contains([1,2,3], 3)
True
>>> contains([1,2,3], 4)
False
这不会很快,因为 bisect
是用Python编写的,而不是用C编写的,所以你可能会发现顺序in
更快很多情况。bisect
自Python 2.4以来在CPython中有一个可选的C加速。
很难确定CPython的确切收支平衡点。这是因为代码是用C语言编写的;如果你检查的值是否大于或小于序列中的任何值,那么CPU的分支预测将对你起作用,你会得到:
In [2]: a = list(range(100))
In [3]: %timeit contains(a, 101)
The slowest run took 8.09 times longer than the fastest. This could mean that an intermediate result is being cached
1000000 loops, best of 3: 370 ns per loop
此处,3中的最佳值不能代表算法的 true 运行时间。
但是通过调整测试,我得出的结论是,对于只有30个元素的列表,二等分可能比in
更快。
但是,如果你正在做很多in
次操作,你应该使用set
;你可以将列表转换成一个集合(它甚至不会被排序),in
操作将比任何二进制搜索都渐近地快:
>>> a = [10, 6, 8, 1, 2, 5, 9]
>>> a_set = set(a)
>>> 10 in a_set
True
另一方面,排序列表的时间复杂度比构建集合的时间复杂度高,因此大部分时间都是一个集合。< / p>