Python 二分查找函数查找排序列表中第一个大于特定值的数字
我正在尝试用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
的数字。
有没有人能推荐一个更简洁的实现方法,能够真正有效地解决这个问题?我本以为这不是个特别复杂的问题,但到目前为止我还没找到解决方案。
2 个回答
0
如果你想要一个不使用 bisect 函数的实现,可以试试下面的代码:
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
22
你试过使用bisect
模块吗?
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
会返回一个错误的索引。