赞
踩
二分法是一种效率比较高的搜索方法,时间复杂度为 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。而在二叉搜索树中,左右子树的节点数量取决于添加的顺序,并不一定满足数量相等或最多相差一个。
要满足二叉搜索树的特性,还要控制左子树和右子树的节点数量差异,就需要对二叉搜索树的“平衡”进行控制,在数据结构中,按这种思路实现的二叉树叫红黑树,后面的文章继续研究。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。