Python实现二分法搜索

Python实现二分法搜索

二分法是一种效率比较高的搜索方法,时间复杂度为 O(log2n) 。

假设有一个1~100之间的数字,你来猜这个数是多少,每猜一次可以得到三种回答:正确、大了或小了。如何保证用最少的次数猜对?很多人会想到先猜50,如果猜大了,说明答案比50小,然后猜25...用这种方法,每次都可以将数字的范围缩小一半,对于1~100之间的任何数,最多都只需要7次就能找到答案。

这种每次将搜索范围缩小一半的方法,就是二分法搜索的思想。本文使用 Python 来实现二分法搜索。

一、Python 二分法搜索递归实现

在实现代码前,先分析二分法的前提条件:

1. 上面的例子在1~100中查找一个数字,每次都要判断是大了还是小了,这里隐含了一个条件,即1~100是升序排列的。对于二分法,数据列表必须是有序的,一般是升序,降序也可以。

2. 跳出1~100的范围,对于任何的数据集合,都可以使用二分法来搜索其中的某个数。

现在来看一下二分法搜索的具体过程。如在 [50, 77, 55, 29, 10, 30, 66, 18, 80, 51] 中搜索 77 。

1. 对列表排序。通常的数据很少是排好序的,要使用二分法,就要先对数据列表进行排序。

2. 取一半位置的数据。对于一个数据集合,数据量可能是奇数,也可能是偶数,但不管奇数偶数,都取2的整除。

所以,这里先找到一半位置的50。

3. 判断中间位置的数字与目标数字的大小,缩小搜索范围,然后重复第2步。

4. 继续重复2和3,直到找到目标数据。

根据搜索的过程,来实现代码。

# coding=utf-8
class BinarySearch(object):
    def binary_search(self, array, data):
        """二分查找法递归实现"""
        if len(array) == 0:
            return False
        array.sort()
        mid_index = len(array) // 2
        if array[mid_index] == data:
            return True
        return self.binary_search(array[mid_index + 1:], data) if data > array[mid_index] else \
            self.binary_search(array[:mid_index], data)

binary_search(array, data):在数据列表 array 中搜索数据 data 。每次递归搜索,数据列表的长度都会缩小“一半”,当找到目标数据或数据列表的长度为0时,递归结束。

if __name__ == '__main__':
    array = [50, 77, 55, 29, 10, 30, 66, 18, 80, 51]
    search = BinarySearch()
    print('搜索结果:', search.binary_search(array, 77))
    print('搜索结果:', search.binary_search(array, 777))

运行结果:

搜索结果: True
搜索结果: False

二、Python 二分法搜索非递归实现

二分法搜索也可以使用非递归的方法实现,还是以在  [50, 77, 55, 29, 10, 30, 66, 18, 80, 51] 中搜索 77 为例。

1. 对列表排序。

2. 取一半位置的数据。这里策略不变,还是取中间位置的数据。但因为是非递归方式,只能通过循环的方式来实现多次二分,如果第一次没有找到目标数据,第二次取一半位置的索引时,就需要根据第一次的判断结果来计算中间索引。因此需要设置两个游标来记录每次二分的开始索引 start 和结束索引 end,如果没有找到目标数据,就修改开始索引或结束索引的值,用于下一次循环中计算中间索引。

3. 根据第一次循环的判断结果,修改开始索引的值,重新计算中间索引和取中间位置的数据。

4. 重复循环直到找到目标数据。当 start 的值等于 end 的值时,范围已经缩小到只剩一个数据,如果继续循环,start 大于 end,说明找不到目标数据,循环结束。

根据这个过程,来实现代码。

    def binary_search_normal(self, array, data):
        """二分查找法非递归实现"""
        array.sort()
        start, end = 0, len(array)-1
        while start <= end:
            mid_index = (start + end) // 2
            if array[mid_index] == data:
                return True
            if data > array[mid_index]:
                start = mid_index + 1
            else:
                end = mid_index - 1
        return False

binary_search_normal(array, data):在数据列表 array 中搜索数据 data 的非递归实现。每次循环,都会根据判断结果修改 start 或 end 的值,下一次循环中根据新的 start 和 end 值计算新的 min_index,直到找到目标数据或找完整个数据列表(start>end),循环结束。

    print('搜索结果:', search.binary_search_normal(array, 77))
    print('搜索结果:', search.binary_search_normal(array, 777))

运行结果:

搜索结果: True
搜索结果: False

三、二分法搜索与二叉搜索树的关系

关于Python实现二叉搜索树,可以参考:https://blog.csdn.net/weixin_43790276/article/details/105753543

如果将上面的数据 [50, 77, 55, 29, 10, 30, 66, 18, 80, 51]  添加到二叉搜索树中,得到的二叉搜索树结构如下。

根据二叉搜索树的特性,在向二叉搜索树中插入数据时,就已经先判断了插入数据与根节点数据的大小,小的插入到左子树中,大的插入到右子树中,所以在二叉搜索树中搜索数据时,可以比较数据与根节点的数据大小,递归判断是在左子树还是在右子树中搜索。每一次递归,都会将范围缩小到左子树或右子树,直到找到目标数据。这种搜索方式与二分法搜索的思路非常相似。

二叉搜索树可以理解为二分法实现的一种数据结构,但并不完全是,因为二叉搜索树只是满足了二分法的思想,与二分法是有区别的。

二分法每次都肯定可以将数据范围缩小“一半”,因为数据长度可能是奇数个或偶数个,二分后的两个数据集合的数量要么相等要么相差1。而在二叉搜索树中,左右子树的节点数量取决于添加的顺序,并不一定满足数量相等或最多相差一个。

要满足二叉搜索树的特性,还要控制左子树和右子树的节点数量差异,就需要对二叉搜索树的“平衡”进行控制,在数据结构中,按这种思路实现的二叉树叫红黑树,后面的文章继续研究。

 

 

  • 16
    点赞
  • 99
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小斌哥ge

非常感谢,祝你一切顺利。

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

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

打赏作者

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

抵扣说明:

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

余额充值