二分查找算法的Python实现(头歌教学实践平台)

第1关:二分查找算法

任务描述

本关任务:编写代码实现二分查找算法。

相关知识

为了完成本关任务,你需要掌握: 1.查找的基本概念; 2.如何实现二分查找。

查找的基本概念

如果数据项被保存在如列表这样的集合中,我们会称这些数据项具有线性或者顺序关系。在 Python 的列表 List 中,这些数据项的存储位置称为下标(index),这些下标都是有序的整数。通过下标,我们可以按照顺序来访问和查找数据项,这种技术称为“顺序查找”。图 1 显示了顺序查找的基本过程,要确定列表中是否存在需要查找的数据项,首先从列表的第 1 个数据项开始,按照下标增长的顺序逐个比对数据项,如果到最后一个都未发现要查找的项,那么查找失败。

 图1 整数列表的顺序查找

要对查找算法进行分析,首先要确定其中的基本计算步骤。在查找算法中,这种基本计算步骤就是进行数据项的比对。比对当前数据项等于还是不等于要查找的数据项,比对的次数决定了算法的时间复杂度。在顺序查找算法中,为了保证讨论的是一般情形,需要假定列表中的数据项并没有按值排列顺序,在列表中各处出现的概率是相同的。对于具有 n 个数据项的列表,所查找的数据项是否在列表中,比对次数是不一样的。

  1. 如果数据项不在列表中,需要比对所有数据项才能得知,比对次数是 n。

  2. 如果数据项在列表中,其情况就较为复杂。最好的情况是第 1 次比对就找到,最坏的情况是要进行 n 次比对。

因为数据项在列表中各个位置出现的概率是相同的,平均状况下比对的次数是 n/2,所以顺序查找的时间复杂度是O(n)

以上我们是假定列表中的数据项是无序的,那么如果数据项排了序,顺序查找算法的效率又如何?在有序表中,当数据项存在时,比对过程与无序表完全相同。不同之处在于,如果数据项不存在,比对可以提前结束。图 2 展示了在有序表中顺序查找数据项 50 的过程,当看到 54 时,可知道后面不可能存在 50,可以提前退出查找。

图2 在有序表中进行顺序查找

实际上,对于有序表,顺序查找的算法复杂度仍然是O(n)。只是在数据项不存在的时候,有序表的查找能节省一些比对次数,但并不改变其数量级。

二分查找

那么对于有序表,有没有更好更快的查找算法?在顺序查找中,如果第 1 个数据项不匹配查找项的话,那最多还有 n-1 个待比对的数据项。为了提升查找效率,我们可以引入二分查找算法,利用有序表的特性,迅速缩小待比对数据项的范围。

二分查找的基本思路为:

设 R[first,…,last] 是当前的查找区间,首先确定该区间的中间位置为 mid=(first+last)/2,然后将待查的数据项 k 与 R[mid]进行比较:

  1. 若相等,则查找成功;

  2. 若 R[mid]>k,则由表的有序性可知 R[mid,…,last] 均大于 k,因此若表中存在该数据项,则一定是在 mid 左边的子表 R[first,…,mid-1] 中,故将其作为新的查找区间;

  3. 若 R[mid]<k,则由表的有序性可知 R[first,…,mid] 均小于 k,因此若表中存在该数据项,则一定是在 mid 右边的子表 R[mid+1,…,last] 中,故将其作为新的查找区间。

无论如何,我们都会将比对范围缩小到原来的一半,然后在新的比对范围中重复这个过程。图 3 展示了在有序表中如何使用二分查找方法快速找到值为 54 的项。首先将有序表中间的项 44 与 54 进行比较,44 比 54 小,则 54 在列表的后半部分,新的比对范围缩小为从 54 到 93 的部分。再将中间项 65 与 54 进行比较,65 比 54 大,则 54 在前半部分,新的比对范围缩小为从 54 到 55 的部分。再将中间项 54 与 所查找的项 54 进行比较,匹配成功,查找结束,总共比较 3 次。

图3 在有序表中进行二分查找

编程要求

在右侧编辑器中的 Begin-End 区间补充代码,根据二分查找的算法思想完成binarySearch函数,实现在有序表中查找数据项。若查找成功,返回 True;若查找失败,返回 False。

测试说明

平台会对你编写的代码进行测试,比对你输出的数值与实际正确的数值,只有所有数据全部计算正确才能通过测试:

测试输入:

  1. 0,1,2,8,13,17,19,32,42

输入说明:输入为需要在其中查找的有序表。

预期输出:

  1. False
  2. False
  3. True
  4. False

输出说明:输出为在有序表中查找数据项 3、7、13、22 的查找结果。若查找成功,返回 True;若查找失败,返回 False。

测试输入:

  1. 2,5,7,9,13,22,30,47,50

预期输出:

  1. False
  2. True
  3. True
  4. True

开始你的任务吧,祝你成功!

'''请在Begin-End之间补充代码, 完成binarySearch函数'''

def binarySearch(alist, item):
    first = 0  # 查找范围第一项的下标
    last = len(alist)-1   # 查找范围最后一项的下标
    found = False

    while first<=last and not found:
        midpoint = (first + last)//2   # 中间项下标
        # 若列表alist的中间项与查找项item相等,则查找成功,found置为True
        # 否则就需要缩小比对范围,分为两种情况:
        # 1.查找项比中间项小,说明查找项在前半部分,更改last
        # 2.查找项比中间项大,说明查找项在后半部分,更改first
        # ********** Begin ********** #    
        if alist[midpoint]==item:            
            found=True        
        else:            
            if item<alist[midpoint]:                
                last=midpoint-1            
            elif item>alist[midpoint]:                
                first=midpoint+1    
        # ********** End ********** #

    return found

 


第2关:二分查找的递归实现

任务描述

本关任务:编写代码使用递归方法实现二分查找。

相关知识

为了完成本关任务,你需要掌握: 1.如何递归实现二分查找; 2.二分查找的算法分析。

递归实现二分查找

二分查找算法实际上体现了解决问题的典型策略:分而治之。该策略如图 1 所示,是将问题分为若干更小规模的部分,通过解决每一个小规模部分问题,并将结果汇总,从而得到原问题的解。

图1 分而治之的过程 

显然,递归算法就是一种典型的分治策略算法,二分查找也适合用递归算法来实现。当我们对一个列表执行二分查找时,我们首先选择中间项。如果查找项比中间项小,我们可以在原来列表的前半部分执行二分查找;同样地,如果查找项更大,我们可以在后半部分执行二分查找。无论怎样,这都是一个对较小的列表实现二分查找的递归调用。

二分查找的算法分析

由于二分查找每次比对都将下一步的比对范围缩小一半,对于 n 个数据项的有序表,比对范围会逐渐减半到 n/2n/4n/8……n/2^{i}。当比对次数足够多以后,比对范围内就会仅剩余 1 个数据项,且无论这个数据项是否匹配查找项,比对最终都会结束。根据等式n/2^{i}=1可求得 i 的值,所以二分法查找的时间复杂度是O(logn)

另外,虽然二分查找在时间复杂度上优于顺序查找,但也要考虑到对数据项进行排序的开销。如果数据集经常变动,查找次数相对较少,那么可能直接对无序表进行顺序查找要更经济。所以,在算法的选择问题上,光看时间复杂度的优劣是不够的,还需要考虑到实际应用的情况。

编程要求

根据提示,在右侧编辑器中的 Begin-End 区间补充代码,完成binarySearch函数,使用递归的方式来对有序表实现二分查找。若查找成功,返回 True;若查找失败,返回 False。

测试说明

平台会对你编写的代码进行测试,比对你输出的数值与实际正确的数值,只有所有数据全部计算正确才能通过测试:

测试输入:

  1. 0,1,2,8,13,17,19,32,42

输入说明:输入为需要在其中查找数据项的有序表。

预期输出:

  1. True
  2. True
  3. True
  4. False

输出说明:输出为在有序表中查找数据项 2、8、13、30 的查找结果。若查找成功,返回 True;若查找失败,返回 False。

测试输入:

  1. 2,5,7,9,13,22,30,47,50

预期输出:

  1. True
  2. False
  3. True
  4. True

提示

  1. list = [0, 1, 2, 3, 4, 5, 6]
  2. print(len(list)//2)
  3. print(list[:3])
  4. print(list[4:])

输出:

  1. 3
  2. [0, 1, 2]
  3. [4, 5, 6]

开始你的任务吧,祝你成功!

'''请在Begin-End之间补充代码, 完成binarySearch函数'''

def binarySearch(alist, item):
    if len(alist) == 0:  # 递归的基本结束条件
        return False
    else:
        midpoint = len(alist)//2
        if alist[midpoint]==item:
          return True
        else:   # 缩小规模
            # 若查找项小于中间项,则需要递归调用自身来实现对前半部分(从最开始到中间)的查找,返回递归的结果
            # 否则查找项大于中间项,则需要递归调用自身来实现对后半部分(从中间到最后)的查找,返回递归的结果
            # ********** Begin ********** #    
            if item<alist[midpoint]:
                return binarySearch(alist[:midpoint],item)
            elif item>alist[midpoint]:
                return binarySearch(alist[midpoint+1:],item)
            # ********** End ********** #

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值