数据结构之二分查找及其变种

二分查找某元素值

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. 直接用上面那个 二分查找不小于该元素的第一个值返回值-1即可
  2. 由于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. 直接用上面那个 二分查找大于该元素的第一个值返回值-1即可
  2. 同理,修改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

总结

  1. bisect.bisect_leftbisect.bisect_right的区别在于对于arr=[2,3,5,5,5,6,8,9], x=5,left会返回第一个5的位置,而right会返回最后一个5的后一位。也就是说,left是在左边插入新值,right是在右边插入。
  2. 所有大于或大于等于x问题,都有可能出现数组中所有值都小于x的情况,此时需要返回索引为len(arr),因此ed的初始值要设置为len(arr)
  3. 同理,所有小于或小于等于x问题,都有可能出现数组中所有值都小于x的情况,此时需要对返回值处理使其在特殊情况下返回-1
  4. 对于st = mid, ed = mid -1这种ed为主动的情况,要设置mid为向上取整。避免st和ed相差1却永远不动导致的循环无法退出问题。

——————————————————————————————————————————
bisect — 数组二分查找算法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值