是否有一个库函数可以对列表/元组执行二分搜索,如果找到则返回该项目的位置,如果没有则返回“False”(-1、None 等)?

我在中找到了函数 bisect_left/right 对分模块, ,但即使该项目不在列表中,它们仍然返回一个位置。这对于他们的预期用途来说非常好,但我只想知道某个项目是否在列表中(不想插入任何内容)。

我想用 bisect_left 然后检查该位置的项目是否等于我正在搜索的项目,但这似乎很麻烦(而且我还需要进行边界检查该数字是否可以大于列表中的最大数字)。如果有更好的方法我想知道。

编辑 为了澄清我需要这个的目的:我知道字典非常适​​合于此,但我试图将内存消耗保持在尽可能低的水平。我的预期用途是一种双向查找表。我在表中有一个值列表,我需要能够根据它们的索引访问这些值。而且我还希望能够找到特定值的索引,或者如果该值不在列表中则找不到索引。

为此使用字典将是最快的方法,但会(大约)使内存需求增加一倍。

我问这个问题时认为我可能忽略了 Python 库中的某些内容。看来我必须按照 Moe 的建议编写自己的代码。

有帮助吗?

解决方案

from bisect import bisect_left

def binary_search(a, x, lo=0, hi=None):  # can't use a to specify default for hi
    hi = hi if hi is not None else len(a)  # hi defaults to len(a)   
    pos = bisect_left(a, x, lo, hi)  # find insertion position
    return (pos if pos != hi and a[pos] == x else -1)  # don't walk off the end

其他提示

为什么不查看 bisect_left/right 的代码并对其进行调整以满足您的目的。

像这样:

def binary_search(a, x, lo=0, hi=None):
    if hi is None:
        hi = len(a)
    while lo < hi:
        mid = (lo+hi)//2
        midval = a[mid]
        if midval < x:
            lo = mid+1
        elif midval > x: 
            hi = mid
        else:
            return mid
    return -1

这有点偏离主题(因为莫伊的回答似乎完整地回答了OP的问题),但可能值得从头到尾看看整个过程的复杂性。如果您将事物存储在排序列表中(这是二分搜索会有所帮助的地方),然后只是检查是否存在,您会遇到(最坏的情况,除非指定):

排序列表

  • O( n log n) 最初创建列表(如果它是未排序的数据。O(n),如果已排序)
  • O( log n) 次查找(这是二分查找部分)
  • O( n ) 插入/删除(平均情况可能是 O(1) 或 O(log n),具体取决于您的模式)

而与 set(), ,你正在招致

  • 创建时间复杂度为 O(n)
  • O(1) 查找
  • O(1) 插入/删除

排序列表真正为您提供的是“下一个”、“上一个”和“范围”(包括插入或删除范围),在给定起始索引的情况下,它们的复杂度为 O(1) 或 O(|range|)。如果您不经常使用这些类型的操作,那么总体而言,存储为集合并排序显示可能是更好的选择。 set() 在 python 中产生很少的额外开销。

可能值得一提的是,bisect 文档现在提供搜索示例:http://docs.python.org/library/bisect.html#searching-sorted-lists

(提高 ValueError 而不是返回 -1 或 None 更符合 Python 风格——例如 list.index() 就是这样做的。但当然,您可以根据您的需要调整示例。)

最简单的是使用 一分为二 并检查一个位置以查看该项目是否在那里:

def binary_search(a,x,lo=0,hi=-1):
    i = bisect(a,x,lo,hi)
    if i == 0:
        return -1
    elif a[i-1] == x:
        return i-1
    else:
        return -1

这是手册中正确的:

http://docs.python.org/2/library/bisect.html

8.5.1.搜索排序列表

上述 bisect() 函数对于查找插入点很有用,但对于常见的搜索任务来说可能会很棘手或尴尬。以下五个函数展示了如何将它们转换为排序列表的标准查找:

def index(a, x):
    'Locate the leftmost value exactly equal to x'
    i = bisect_left(a, x)
    if i != len(a) and a[i] == x:
        return i
    raise ValueError

因此,稍微修改一下你的代码应该是:

def index(a, x):
    'Locate the leftmost value exactly equal to x'
    i = bisect_left(a, x)
    if i != len(a) and a[i] == x:
        return i
    return -1

我同意 @DaveAbrahams 的回答 使用 bisect 模块是正确的方法。他在回答中没有提及任何重要细节。

来自 文档 bisect.bisect_left(a, x, lo=0, hi=len(a))

二分模块不需要提前预先计算搜索数组。您只需将端点呈现给 bisect.bisect_left 而不是使用默认值 0len(a).

对于我的使用来说更重要的是,寻找一个值 X 以使给定函数的误差最小化。为此,我需要一种方法让 bisect_left 的算法调用我的计算。这真的很简单。

只需提供一个定义的对象 __getitem__ 作为 a

例如,我们可以使用二分算法来求任意精度的平方根!

import bisect

class sqrt_array(object):
    def __init__(self, digits):
        self.precision = float(10**(digits))
    def __getitem__(self, key):
        return (key/self.precision)**2.0

sa = sqrt_array(4)

# "search" in the range of 0 to 10 with a "precision" of 0.0001
index = bisect.bisect_left(sa, 7, 0, 10*10**4)
print 7**0.5
print index/(10**4.0)

如果您只想查看它是否存在,请尝试将列表转换为字典:

# Generate a list
l = [n*n for n in range(1000)]

# Convert to dict - doesn't matter what you map values to
d = dict((x, 1) for x in l)

count = 0
for n in range(1000000):
    # Compare with "if n in l"
    if n in d:
        count += 1

在我的机器上,“if n in l”花了 37 秒,而“if n in d”花了 0.4 秒。

这个是:

  • 不是递归的(这使得它更 内存效率高 比大多数递归方法)
  • 实际上 在职的
  • 自从它快 运行时没有任何不必要的 如果是 和条件
  • 基于数学断言(低 + 高)/2 的下限 总是小于 高的 在哪里 低的 是下限并且 高的 是上限。
  • 测试过:D

def binsearch(t, key, low = 0, high = len(t) - 1):
    # bisecting the range
    while low < high:
        mid = (low + high)//2
        if t[mid] < key:
            low = mid + 1
        else:
            high = mid
    # at this point 'low' should point at the place
    # where the value of 'key' is possibly stored.
    return low if t[low] == key else -1

戴夫·亚伯拉罕的解决方案很好。虽然我会做得很简约:

def binary_search(L, x):
    i = bisect.bisect_left(L, x)
    if i == len(L) or L[i] != x:
        return -1
    return i

虽然 Python 中没有明确的二分搜索算法,但有一个模块 - bisect - 旨在使用二分搜索查找排序列表中元素的插入点。这可以被“欺骗”以执行二分搜索。这样做的最大优点与大多数库代码具有相同的优点 - 它具有高性能、经过良好测试并且可以正常工作(特别是二分搜索可以 成功实施相当困难 - 特别是如果没有仔细考虑边缘情况)。

基本类型

对于字符串或整数等基本类型,这非常简单 - 您所需要的只是 bisect 模块和排序列表:

>>> import bisect
>>> names = ['bender', 'fry', 'leela', 'nibbler', 'zoidberg']
>>> bisect.bisect_left(names, 'fry')
1
>>> keyword = 'fry'
>>> x = bisect.bisect_left(names, keyword)
>>> names[x] == keyword
True
>>> keyword = 'arnie'
>>> x = bisect.bisect_left(names, keyword)
>>> names[x] == keyword
False

您还可以使用它来查找重复项:

...
>>> names = ['bender', 'fry', 'fry', 'fry', 'leela', 'nibbler', 'zoidberg']
>>> keyword = 'fry'
>>> leftIndex = bisect.bisect_left(names, keyword)
>>> rightIndex = bisect.bisect_right(names, keyword)
>>> names[leftIndex:rightIndex]
['fry', 'fry', 'fry']

显然,如果需要,您可以只返回索引而不是该索引处的值。

对象

对于自定义类型或对象,事情有点棘手:您必须确保实现丰富的比较方法才能正确平分比较。

>>> import bisect
>>> class Tag(object):  # a simple wrapper around strings
...     def __init__(self, tag):
...         self.tag = tag
...     def __lt__(self, other):
...         return self.tag < other.tag
...     def __gt__(self, other):
...         return self.tag > other.tag
...
>>> tags = [Tag('bender'), Tag('fry'), Tag('leela'), Tag('nibbler'), Tag('zoidbe
rg')]
>>> key = Tag('fry')
>>> leftIndex = bisect.bisect_left(tags, key)
>>> rightIndex = bisect.bisect_right(tags, key)
>>> print([tag.tag for tag in tags[leftIndex:rightIndex]])
['fry']

这应该至少适用于 Python 2.7 -> 3.3

使用字典不会使内存使用量增加一倍,除非您存储的对象非常小,因为这些值只是指向实际对象的指针:

>>> a = 'foo'
>>> b = [a]
>>> c = [a]
>>> b[0] is c[0]
True

在该示例中,“foo”仅存储一次。这对你有影响吗?无论如何,我们到底在谈论多少项目?

此代码以递归方式处理整数列表。寻找最简单的案例场景,即:列表长度小于 2。这意味着答案已经存在,并且将进行测试以检查答案是否正确。如果不是,则设置一个中间值并测试是否正确,如果不是,则通过再次调用该函数来执行二分,但将中间值设置为上限或下限,通过向左或向右移动它

def binary_search(intList, intValue, lowValue, highValue):
    if(highValue - lowValue) < 2:
        return intList[lowValue] == intValue or intList[highValue] == intValue
    middleValue = lowValue + ((highValue - lowValue)/2)
    if intList[middleValue] == intValue:
        return True
    if intList[middleValue] > intValue:
        return binary_search(intList, intValue, lowValue, middleValue - 1)
   return binary_search(intList, intValue, middleValue + 1, highValue)

查看维基百科上的示例 http://en.wikipedia.org/wiki/Binary_search_algorithm

def binary_search(a, key, imin=0, imax=None):
    if imax is None:
        # if max amount not set, get the total
        imax = len(a) - 1

    while imin <= imax:
        # calculate the midpoint
        mid = (imin + imax)//2
        midval = a[mid]

        # determine which subarray to search
        if midval < key:
            # change min index to search upper subarray
            imin = mid + 1
        elif midval > key:
            # change max index to search lower subarray
            imax = mid - 1
        else:
            # return index number 
            return mid
    raise ValueError
'''
Only used if set your position as global
'''
position #set global 

def bst(array,taget): # just pass the array and target
        global position
        low = 0
        high = len(array)
    while low <= high:
        mid = (lo+hi)//2
        if a[mid] == target:
            position = mid
            return -1
        elif a[mid] < target: 
            high = mid+1
        else:
            low = mid-1
    return -1

我想这更好、更有效。请纠正我:)。谢谢

  • s 是一个列表。
  • binary(s, 0, len(s) - 1, find) 是初始呼叫。
  • 函数返回查询项的索引。如果没有这样的项目,则返回 -1.

    def binary(s,p,q,find):
        if find==s[(p+q)/2]:
            return (p+q)/2
        elif p==q-1 or p==q:
            if find==s[q]:
                return q
            else:
                return -1
        elif find < s[(p+q)/2]:
            return binary(s,p,(p+q)/2,find)
        elif find > s[(p+q)/2]:
            return binary(s,(p+q)/2+1,q,find)
    
def binary_search_length_of_a_list(single_method_list):
    index = 0
    first = 0
    last = 1

    while True:
        mid = ((first + last) // 2)
        if not single_method_list.get(index):
            break
        index = mid + 1
        first = index
        last = index + 1
    return mid

二分查找:

// List - values inside list
// searchItem - Item to search
// size - Size of list
// upperBound - higher index of list
// lowerBound - lower index of list
def binarySearch(list, searchItem, size, upperBound, lowerBound):
        print(list)
        print(upperBound)
        print(lowerBound)
        mid = ((upperBound + lowerBound)) // 2
        print(mid)
        if int(list[int(mid)]) == value:
               return "value exist"
        elif int(list[int(mid)]) < value:
             return searchItem(list, value, size, upperBound, mid + 1)
        elif int(list[int(mid)]) > value:
               return searchItem(list, value, size, mid - 1, lowerBound)

// 要调用上面的函数,请使用:

list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
searchItem = 1        
print(searchItem(list[0], item, len(list[0]) -1, len(list[0]) - 1, 0))

我需要 python 中的二分搜索和 Django 模型的通用。在 Django 模型中,一个模型可以具有另一个模型的外键,我想对检索到的模型对象执行一些搜索。我写了下面的函数你可以使用它。

def binary_search(values, key, lo=0, hi=None, length=None, cmp=None):
    """
    This is a binary search function which search for given key in values.
    This is very generic since values and key can be of different type.
    If they are of different type then caller must specify `cmp` function to
    perform a comparison between key and values' item.
    :param values:  List of items in which key has to be search
    :param key: search key
    :param lo: start index to begin search
    :param hi: end index where search will be performed
    :param length: length of values
    :param cmp: a comparator function which can be used to compare key and values
    :return: -1 if key is not found else index
    """
    assert type(values[0]) == type(key) or cmp, "can't be compared"
    assert not (hi and length), "`hi`, `length` both can't be specified at the same time"

    lo = lo
    if not lo:
        lo = 0
    if hi:
        hi = hi
    elif length:
        hi = length - 1
    else:
        hi = len(values) - 1

    while lo <= hi:
        mid = lo + (hi - lo) // 2
        if not cmp:
            if values[mid] == key:
                return mid
            if values[mid] < key:
                lo = mid + 1
            else:
                hi = mid - 1
        else:
            val = cmp(values[mid], key)
            # 0 -> a == b
            # > 0 -> a > b
            # < 0 -> a < b
            if val == 0:
                return mid
            if val < 0:
                lo = mid + 1
            else:
                hi = mid - 1
    return -1

上面有很多好的解决方案,但我还没有看到一个简单的(KISS 保持简单(因为我)愚蠢地使用 Python 内置/通用二分函数来进行二分搜索。通过围绕二分函数的一些代码,我想我在下面有一个示例,其中我测试了一个小字符串数组的所有情况。上面的一些解决方案暗示/说了这一点,但希望下面的简单代码能够帮助像我一样困惑的人。

Python bisect 用于指示将新值/搜索项插入到排序列表中的位置。下面的代码使用 bisect_left ,如果在列表/数组中找到搜索项,则返回命中的索引(注意 bisect 和 bisect_right 将返回命中或匹配后的元素的索引作为插入点)如果未找到, bisect_left 将返回排序列表中下一项的索引,该索引不会 == 搜索值。唯一的另一种情况是,搜索项将位于列表的末尾,其中返回的索引将超出列表/数组的末尾,并且在下面的代码中,Python 使用“and”逻辑句柄提前退出。(第一个条件False Python不检查后续条件)

#Code
from bisect import bisect_left
names=["Adam","Donny","Jalan","Zach","Zayed"]
search=""
lenNames = len(names)
while search !="none":
    search =input("Enter name to search for or 'none' to terminate program:")
    if search == "none":
        break
    i = bisect_left(names,search)
    print(i) # show index returned by Python bisect_left
    if i < (lenNames) and names[i] == search:
        print(names[i],"found") #return True - if function
    else:
        print(search,"not found") #return False – if function
##Exhaustive test cases:
##Enter name to search for or 'none' to terminate program:Zayed
##4
##Zayed found
##Enter name to search for or 'none' to terminate program:Zach
##3
##Zach found
##Enter name to search for or 'none' to terminate program:Jalan
##2
##Jalan found
##Enter name to search for or 'none' to terminate program:Donny
##1
##Donny found
##Enter name to search for or 'none' to terminate program:Adam
##0
##Adam found
##Enter name to search for or 'none' to terminate program:Abie
##0
##Abie not found
##Enter name to search for or 'none' to terminate program:Carla
##1
##Carla not found
##Enter name to search for or 'none' to terminate program:Ed
##2
##Ed not found
##Enter name to search for or 'none' to terminate program:Roger
##3
##Roger not found
##Enter name to search for or 'none' to terminate program:Zap
##4
##Zap not found
##Enter name to search for or 'none' to terminate program:Zyss
##5
##Zyss not found
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top