二分查找某元素值
def binary_find(arr, x):
st, ed = 0, len(arr)-1
while st <= ed:
mid = (st+ed)//2
if arr[mid] == x:
return mid # 找到并返回索引
elif arr[mid] > x:
ed = mid - 1
else:
st = mid + 1
return -1 # 代表没找到
二分查找不小于该元素的第一个值 / 二分左边插入新值
如[2,3,5,5,5,6,8,9]
中查找5,希望返回第一个5对应的索引 idx=2
等价于python现有模块:bisect.bisect_left(a, x, lo=0, hi=len(a))
注意:因为可能元素中所有数组都小于该值,此时索引应该返回数组长度,因此,ed的初始值为len(arr)而不是前面的len(arr)-1
# 推荐!!!!
# ed初始化为len(arr)-1的写法,可以囊括上面所说的情况
def binary_left(arr, x):
st, ed = 0, len(arr)
while st < ed:
mid = (st+ed)//2
if arr[mid] < x:
st = mid + 1
else:
ed = mid
return st
# ed初始化为len(arr)-1的写法,此时要对返回值做判断
def binary_left(arr, x):
st, ed = 0, len(arr)-1
while st < ed:
mid = (st+ed)//2
if arr[mid] >= x:
ed = mid
else:
st = mid + 1
return st if not arr or arr[st] >= x else st+1 # 这是为了包括元素中所有数组都小于该值的情况
二分查找小于该元素的最后一个值
如[2,3,5,5,5,6,8,9]
中查找5,希望返回3(最后一个小于5的元素)对应的索引 idx=1
。特别地,若数组中元素都大于x,则返回-1。
实现方法有两种
- 直接用上面那个 二分查找不小于该元素的第一个值 的返回值-1即可
- 由于st和ed相差1时,mid始终等于st,特别是当st<x,而ed>=x,st永远不动,无法循环退出条件。为避免这种情况,需要修改mid为向上取整,使得mid指向ed,而ed占据主动权,可以越过mid与st重叠。
def binary_left(arr, x):
st, ed = 0, len(arr)
while st < ed:
mid = (st+ed)//2
if arr[mid] >= x:
ed = mid
else:
st = mid + 1
return st - 1
def binary_left(arr, x):
st, ed = 0, len(arr)-1
while st < ed:
mid = (st+ed)//2 + 1
if arr[mid] < x:
st = mid
else:
ed = mid - 1
return st if arr[st]<x else -1 # 若arr中所有元素都大于x,则返回-1
二分查找大于该元素的第一个值 / 二分右边插入新值
如[2,3,5,5,5,6,8,9]
中查找5,希望返回6对应的索引 idx=5
等价于python现有模块:bisect.bisect_right(a, x, lo=0, hi=len(a))
注意:因为可能元素中所有数组都小于该值,此时索引应该返回数组长度,因此,ed的初始值为len(arr)而不是前面的len(arr)-1
def binary_right(arr, x):
st, ed = 0, len(arr)
while st < ed:
mid = (st+ed)//2
if arr[mid] <= x:
st = mid + 1
else:
ed = mid
return st
二分查找不大于该元素的最后一个值
如[2,3,5,5,5,6,8,9]
中查找5,希望返回最后一个5对应的索引 idx=4
。特别地,若数组中元素都大于x,则返回-1。
- 直接用上面那个 二分查找大于该元素的第一个值 的返回值-1即可
- 同理,修改mid为向上取整
def binary_right(arr, x):
st, ed = 0, len(arr)
while st < ed:
mid = (st+ed)//2
if arr[mid] <= x:
st = mid + 1
else:
ed = mid
return st-1
def binary_right(arr, x):
st, ed = 0, len(arr)-1
while st < ed:
mid = (st+ed)//2 + 1
if arr[mid] <= x:
st = mid
else:
ed = mid - 1
return st if arr[st]<=x else -1
总结
bisect.bisect_left
和bisect.bisect_right
的区别在于对于arr=[2,3,5,5,5,6,8,9], x=5
,left会返回第一个5的位置,而right会返回最后一个5的后一位。也就是说,left是在左边插入新值,right是在右边插入。- 所有大于或大于等于x问题,都有可能出现数组中所有值都小于x的情况,此时需要返回索引为
len(arr)
,因此ed
的初始值要设置为len(arr)
- 同理,所有小于或小于等于x问题,都有可能出现数组中所有值都小于x的情况,此时需要对返回值处理使其在特殊情况下返回-1
- 对于
st = mid, ed = mid -1
这种ed为主动的情况,要设置mid为向上取整。避免st和ed相差1却永远不动导致的循环无法退出问题。
——————————————————————————————————————————
bisect — 数组二分查找算法