关于二进制搜索有点困惑

时间:2014-03-22 03:51:27

标签: python-2.7

我在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.我该如何解决这个问题?

2 个答案:

答案 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