二分查找

二分查找(binary_search)

仅当列表为有序且为顺序存储时,二分查找才适用
如果一个序列是无序的或者是链表,那么该序列就不能进行二分查找。之所以被查找的序列要满足这样的条件,是由二分查找算法的原理决定的。

本文就来探究几个最常用的二分查找场景:寻找一个数在数组中的下标、寻找左侧边界、寻找右侧边界。

二分查找中的几个细节:

while循环中的不等号是否应该带等号;
mid 是否应该加一

python版二分查找
# 二分查找:在给定数组中,寻找特定元素的下标
def binary_search(list, elem):
    '''
    :param list:  ordered one dimensional array
    :param elem: need to search
    :return: elem's index in list
    '''
    low = 0;
    high = len(list) - 1;
    while low<=high:
        mid = (high+low)//2;  #如果(low+high)不是整数,自动将mid向下取整
        if elem == list[mid]:
            return mid
        elif elem>list[mid]:
            low = mid+1;
        else:
            high = mid - 1
    return None

list  = [1,2,3,4,5]
list1 = [1,2,3,4,5,6]
elem1Index = binary_search(list,3)
elem2Index = binary_search(list1,4)
print(elem1Index, elem2Index)#  2 3

细节详解

  1. while 循环的条件中是 <=,而不是 < ?

初始化时, low 的赋值为第一个元素的索引 0; high 的赋值是最后一个1元素的索引 list.length - 1。

算法的搜索区间(search space)为: [low, high]

while(low <= high)的终止条件是 low == high + 1,写成区间的形式就是 [high+ 1, high],或者带个具体的数字进去 [3, 2],可见这时候搜索区间为空,因为没有数字既大于等于 3 又小于等于 2 的吧。所以这时候 while 循环终止是正确的,直接返回 None 即可。

while(low < high)的终止条件是 low == high,写成区间的形式就是 [high, high],或者带个具体的数字进去 [2, 2],这时候搜索区间非空,还有一个数 2,但此时 while 循环终止了。也就是说这区间 [2, 2] 被漏掉了,索引 2 没有被搜索,如果这时候直接返回 -1 就可能出现错误。

算法的性能

折半查找的运行过程可以用二叉树来描述,这棵树通常称为“判定树”。例如 [ 5 13 19 21 37 56 64 75 80 88 92 ] 对应的判定树如图

在这里插入图片描述

在判定树中可以看到,如果想在查找表中查找 21 的位置,只需要进行 3 次比较,依次和 56、19、21 进行比较,而比较的次数恰好是该关键字所在判定树中的层次(关键字 21 在判定树中的第 3 层)。

对于具有 n 个结点(查找表中含有 n 个关键字)的判定树,它的层次数至多为: log ⁡ 2 x + 1 {{\log }_{2}}x+1 log2x+1(如果结果不是整数,则做取整操作,例如: log ⁡ 2 11 + 1 = 3 + 1 = 4 {{\log_2}11} +1 = 3 + 1 = 4 log211+1=3+1=4 )。

同时,在查找表中各个关键字被查找概率相同的情况下,折半查找的平均查找长度为: A S L = l o g 2 ( n + 1 ) – 1 ASL = log_2(n+1) – 1 ASL=log2(n+1)1

空间复杂度

在我们的实现中,二分查找对于存储空间的要求是只需要能存储low、high、mid、target、数组地址(参数array)和数组元素数量(参数n)这6个局部变量就行了,因此它对存储空间的要求是常数数量,不随着元素多少而变化。所以它的空间复杂度为O(1)。

算法局限性

(1) 需要寻找的特定元素在有序数组存在多个时,无法确定返回第一个特定元素的下标。

例:

list2 = [1,2,2,2,2,5]
elem3Index = binary_search(list2,2)
print(elem3Index) # 2  # 注意此时返回的不是首个 2 的索引

此时算法返回的索引是 2。但是一般情况下我们需要的第一个特定元素1的小标,正确结果应该是 1 。

这样的需求很常见。你也许会说,找到一个 target 索引,然后向左或向右线性搜索不行吗?可以,但是不好,因为这样难以保证二分查找对数级的时间复杂度了。

(2) 该算法的使用的前提是静态查找表中的数据必须是有序的。
同时仅限于查找表用顺序存储结构表示。当查找表使用链式存储结构表示时,折半查找算法无法有效地进行比较操作(排序和查找操作的实现都异常繁琐)。

寻找左侧边界的二分搜索

即寻找第一个特定元素的索引

待续。。。。。。

寻找右侧边界的二分搜索

即寻找最后一个特定元素的索引

https://www.cnblogs.com/kyoner/p/11080078.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

何为xl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值