Python二分搜索类函数,用于查找排序列表中大于特定值的第一个数字

Posted

技术标签:

【中文标题】Python二分搜索类函数,用于查找排序列表中大于特定值的第一个数字【英文标题】:Python binary search-like function to find first number in sorted list greater than a specific value 【发布时间】:2011-04-03 03:48:57 【问题描述】:

我正在尝试在 Python 中编写一个函数,该函数在排序列表中找到大于我作为参数传入的特定值的第一个数字。我在网上找到了使用简单列表推导来实现此目的的示例,但出于我的目的,我需要经常在大型列表上执行此操作,因此以线性时间运行的搜索成本太高。

尽管我遇到了一些无法正常工作的边缘情况,但我在编写类似迭代二分搜索的函数来实现这一点方面已经有所突破。顺便说一句,该函数不需要处理列表中没有更大项目的情况。这是我现有的功能:

def findFirstLarger(num, sortedList):
    low = 0; 
    high = len(sortedList) - 1

    mid = -1
    while True:
        print("low: " + str(low) + "\t high: " + str(high))
        if (low > high):
            print("Ah geez, low is " + str(low) + " and high is " + str(high))
            return # debugging, don't want this to happen
        if low == high:
            return sortedList[low]
        else:
            mid = (low + high) / 2;
            if num == sortedList[mid]:
                return sortedList[mid]
            elif num > sortedList[mid]:
                low = mid + 1
            else:
                high = mid - 1

我注意到此功能不起作用的一个情况如下:

>>> somenumbers=[n*2 for n in range(131072)]
>>> somenumbers[-5:]
[262134, 262136, 262138, 262140, 262142]


>>> binsearch.findFirstLarger(262139,somenumbers)
low: 0   high: 131071
low: 65536   high: 131071
low: 98304   high: 131071
low: 114688  high: 131071
low: 122880  high: 131071
low: 126976  high: 131071
low: 129024  high: 131071
low: 130048  high: 131071
low: 130560  high: 131071
low: 130816  high: 131071
low: 130944  high: 131071
low: 131008  high: 131071
low: 131040  high: 131071
low: 131056  high: 131071
low: 131064  high: 131071
low: 131068  high: 131071
low: 131070  high: 131071
low: 131070  high: 131069
Ah geez, low is 131070 and high is 131069

这里正确的结果是262140,因为这是列表中大于262139的第一个数字。

任何人都可以推荐一个更干净的实现吗?我没想到这会是一个如此深奥的问题,尽管我至今还没有找到任何解决方案。

【问题讨论】:

【参考方案1】:

你试过bisect module吗?

def find_ge(a, key):
    '''Find smallest item greater-than or equal to key.
    Raise ValueError if no such item exists.
    If multiple keys are equal, return the leftmost.

    '''
    i = bisect_left(a, key)
    if i == len(a):
        raise ValueError('No item found with key at or above: %r' % (key,))
    return a[i]

find_ge(somenumbers, 262139)

您的代码错误,即 (1) low > high 是一个有效的终止案例。 (2) 你不应该停在low == high,例如当num == 3 为您的somenumbers 时,它将返回不正确的索引。

【讨论】:

你说得对,我不小心在我的帖子中输入了“数字”而不是“排序列表”。我已经解决了这个问题,现在还提供了一个具体示例,说明它无法按预期工作。 bisect 看起来也很成功。【参考方案2】:

如果你需要没有对分函数的实现,你可以试试下面的代码:

def findFirstLargerOrEqual(num, sortedList):
    '''Finds the smallest index in the sortedList
    of the element which is greater-than or equal to num'''

    slen = len(sortedList)
    start = 0

    while slen > 0:
        m = start + slen//2

        if sortedList[m] < num:
            slen = slen - (m+1 - start)
            start = m+1
            continue

        if start < m and sortedList[m-1] >= num:
            slen = m - start
            continue

        return somenumbers[m]

    raise ValueError('Not found')

somenumbers=[n*2 for n in range(131072)]
print(findFirstLargerOrEqual(262139, somenumbers)) #output: 262140

【讨论】:

由于某些数字未定义,因此无法编译。

以上是关于Python二分搜索类函数,用于查找排序列表中大于特定值的第一个数字的主要内容,如果未能解决你的问题,请参考以下文章

利用二分查找法来解一道列表排序题

二分搜索算法

Leetcode练习(Python):二分查找类:第230题:二叉搜索树中第K小的元素:给定一个二叉搜索树,编写一个函数 kthSmallest 来查找其中第 k

Leetcode练习(Python):二分查找类:第230题:二叉搜索树中第K小的元素:给定一个二叉搜索树,编写一个函数 kthSmallest 来查找其中第 k

python常用的简单算法,二分查找冒泡排序数组翻转等

什么时候顺序搜索比二分搜索好?