我在Python中实现了二进制搜索:
def bisect(seq, goal, lo=0, hi=None):
if hi == None:
hi = len(seq)
while True:
middle = (lo+hi)//2
if seq[middle] == goal:
return middle
elif goal < seq[middle]: hi = middle
elif goal > seq[middle]: lo = middle+1
if lo >= hi:
return -1
它应该返回首次遇到的项目的索引。但是,当我将它应用于这样的列表时:
seq = [-81, -81, 1, 2, 9, 10, 63, 79]
bisect(seq, -81)
它不会返回0但是1.我该如何解决这个问题?
答案 0 :(得分:1)
if seq[middle] == goal: return middle
在没有考虑 lower 索引处是否会出现相同值的情况下退出。在您的示例中,lo
保持为0,hi
变为7,然后为3.当hi
为3时,middle
为1,并且符合您的条件,因此返回1。由于goal
的任何多次出现都必须是连续的,以满足seq
非减少的条件(二进制搜索所需),最简单的方法可能是:
if seq[middle] == goal:
while middle > lo and seq[middle - 1] == goal:
middle = middle - 1
return middle
答案 1 :(得分:1)
对于这样一个看似简单的问题,尽管如此,准确地确定边界条件可能是一个挑战。因此,在这些情况下我通常做的只是使用,或者更常见的是,复制和调整bisect
模块中的代码。您想要的函数是bisect_left
,因为如果有多个索引,则需要最左边的索引,如果没有匹配,则需要插入点的索引。
以下是Python 3.3 Std Lib中的bisect_left
函数的副本:
def bisect_left(a, x, lo=0, hi=None):
"""Return the index where to insert item x in list a, assuming a is sorted.
The return value i is such that all e in a[:i] have e < x, and all e in
a[i:] have e >= x. So if x already appears in the list, a.insert(x) will
insert just before the leftmost x already there.
Optional args lo (default 0) and hi (default len(a)) bound the
slice of a to be searched.
"""
if lo < 0:
raise ValueError('lo must be non-negative')
if hi is None:
hi = len(a)
while lo < hi:
mid = (lo+hi)//2
if a[mid] < x: lo = mid+1
else: hi = mid
return lo