• 一文读懂二分查找(插入)算法


    二分法


    不知道大家有没有玩过一个猜数字的游戏?玩家一给出一个区间和一个数字,由玩家二来猜这个数字,区间会随着玩家二猜的数字发生变化。

    在不考虑表情变化或者别的影响因素时,玩家二要如何去猜这个数字呢?

    下面我们要介绍的,就是这个游戏的万能解法----二分法,也叫做二分查找法


    定义

    二分查找(英语:binary search),也称折半搜索(英语:half-interval search)、对数搜索(英语:logarithmic search),是用来在一个有序数组中查找某一元素的算法。

    过程

    以在一个升序数组中查找一个数为例。

    它每次考察数组当前部分的中间元素,如果中间元素刚好是要找的,就结束搜索过程;如果中间元素小于所查找的值,那么左侧的只会更小,不会有所查找的元素,只需到右侧查找;如果中间元素大于所查找的值同理,只需到左侧查找。

    关键元素

    • 排好序的列表list

    • 左区间left

    • 右区间right

    • 中间点mid

    • 目标值target

    性质

    时间复杂度

    二分查找的最优时间复杂度为 O ( 1 ) O(1) O(1)

    二分查找的平均时间复杂度和最坏时间复杂度均为 O ( l o g n ) O(logn) O(logn) 。因为在二分搜索过程中,算法每次都把查询的区间减半,所以对于一个长度为 n n n的数组,至多会进行 O ( l o g n ) O(logn) O(logn)次查找。

    空间复杂度

    迭代版本的二分查找的空间复杂度为 O ( 1 ) O(1) O(1)

    递归(无尾调用消除)版本的二分查找的空间复杂度为 O ( l o g n ) O(logn) O(logn)


    Tips

    二分法的演变过程如下图所示

    在这里插入图片描述

    通过不断的修正区间,达到找目标值的目的。

    二分查找

    def Bisection(tar):
        weight=[1<<i for i in range(1,10)]
        print("Weight is ",weight)
        l,r=0,len(weight)-1
        
        while l<=r:
            # 确定中心点
            mid=(r-l)//2+l
            # 修改边界
            if weight[mid]==tar:
                # 找到了中心点
                return mid
            if weight[mid]<tar:
                # 目标值在右边
                # 此时需要把区间往右移动
                l=mid+1
            else:
                r=mid-1
        return -1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    我们来看看输出情况

    print(Bisection(512))
    print(Bisection(127))
    print(Bisection(128))
    print(Bisection(0))
    print(Bisection(513))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    Weight is  [2, 4, 8, 16, 32, 64, 128, 256, 512]
    8
    Weight is  [2, 4, 8, 16, 32, 64, 128, 256, 512]
    -1
    Weight is  [2, 4, 8, 16, 32, 64, 128, 256, 512]
    6
    Weight is  [2, 4, 8, 16, 32, 64, 128, 256, 512]
    -1
    Weight is  [2, 4, 8, 16, 32, 64, 128, 256, 512]
    -1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    可以发现,目标值都被正确的输出了,而不在范围内的值,则是以-1进行输出。


    二分插入

    向右边插入

    这个问题的关键在于,当tar==mid的时候,我们让左区间往右走。于是,当出现相同值的时候,插入位置被修正为右边。

    def Bisection(tar):
        weight=[1<<i for i in range(1,10)]
        print("Weight is ",weight)
        l,r=0,len(weight)-1
    
        while l<=r:
            # 确定中心点
            mid=(r-l)//2+l
            # 修改边界
            if weight[mid]<=tar:
                # 此时需要把左区间往右移动
                l=mid+1
            else:
                r=mid-1
        return l
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    Weight is  [2, 4, 8, 16, 32, 64, 128, 256, 512]
    9
    Weight is  [2, 4, 8, 16, 32, 64, 128, 256, 512]
    6
    Weight is  [2, 4, 8, 16, 32, 64, 128, 256, 512]
    7
    Weight is  [2, 4, 8, 16, 32, 64, 128, 256, 512]
    0
    Weight is  [2, 4, 8, 16, 32, 64, 128, 256, 512]
    9
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    诶,我们发现,512的插入位置在9号,128的插入位置在7号。

    向左插入

    同理啦,我们只需要在tar==n[mid]的时候,把右区间往左移,从而实现拿到左端点的情况。

    def Bisection(tar):
        weight=[1<<i for i in range(1,10)]
        print("Weight is ",weight)
        l,r=0,len(weight)-1
    
    
        while l<=r:
            # 确定中心点
            mid=(r-l)//2+l
            # 修改边界
            if weight[mid]>=tar:
                # 此时需要把右区间往左移动
                r=mid-1
            else:
                l=mid+1
        return l
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    Weight is  [2, 4, 8, 16, 32, 64, 128, 256, 512]
    8
    Weight is  [2, 4, 8, 16, 32, 64, 128, 256, 512]
    6
    Weight is  [2, 4, 8, 16, 32, 64, 128, 256, 512]
    6
    Weight is  [2, 4, 8, 16, 32, 64, 128, 256, 512]
    0
    Weight is  [2, 4, 8, 16, 32, 64, 128, 256, 512]
    9
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    可以看到,512被插入到了8号位,513则被插入到了9号位。

  • 相关阅读:
    【笔试刷题训练】day_08
    数据分析技能点-离散程度度量
    4个有影响力的绩效管理最佳实践
    java---并查集算法_食物链(每日一道算法2022.8.17)
    【Node.js】 第三章 http模块
    中国人民大学与加拿大女王大学金融硕士——投资自己,才是当下最稳健的投资
    欧几里得算法证明,最小公倍数求法证明
    JSTL标签库
    pinia中使用@vueuse/core库的useStorage做数据的持久化存储
    componentone/activex-ui-controls-老树发新芽
  • 原文地址:https://blog.csdn.net/qq_45957458/article/details/127709133