排序与查找-查找
[TOC]
顺序查找Sequential Search
无序表
- 如果数据项保存在如列表这样的集合中,我们会称这些数据项具有线性或者顺序关系。
- 在Python List中, 这些数据项的存储位置称为下标(index) , 这些下标都是有序的整数。
通过下标, 我们就可以按照顺序来访问和查找数据项, 这种技术称为“顺序查找
要确定列表中是否存在需要查找的数据项
- 首先从列表的第1个数据项开始,
- 按照下标增长的顺序,逐个比对数据项,如果到最后一个都未发现要查找的项,那么查找失败
1 | def sequentialSearch(alist, item): |
顺序查找的算法复杂度是O(n)
Case | Best Case | Worst Case | Average Case |
---|---|---|---|
item is present | 1 | n | n/2 |
item is not present | n | n | n |
顺序查找:有序表
当数据项存在时,比对过程与无序表完全相同不同之处在于,如果数据项不存在,比对可以提前结束
1 | def orderedSequentialSearch(alist, item): |
顺序查找有序表的各种情况分析
Case | Best Case | Worst Case | Average Case |
---|---|---|---|
item is present | 1 | n | n/2 |
item is not present | 1 | n | n/2 |
- 实际上, 就算法复杂度而言, 仍然是O(n)
- 只是在数据项不存在的时候, 有序表的查找能节省一些比对次数, 但并不改变其数量级
二分查找
从列表中间开始比对
如果列表中间的项匹配查找项,则查找结束
如果不匹配,那么就有两种情况:
- 列表中间项比查找项大,那么查找项只可能出现在前半部分
- 列表中间项比查找项小,那么查找项只可能出现在后半部分
- 无论如何,我们都会将比对范围缩小到原来的一半: n/2
- 继续采用上面的方法查找,每次都会将比对范围缩小一半
1 | def binarySearch(alist, item): |
分而治之
二分查找算法实际上体现了解决问题的典型策略: 分而治之,将问题分为若干更小规模的部分,通过解决每一个小规模部分问题,并将结果汇总,得到原问题的解
二分查找 - 递归
1 | def binarySearch(alist, item): |
由于二分查找, 每次比对都将下一步的比对范围缩小一半,每次比对后剩余数据项如下表所示:
Comparisons | Approximate Number of Items Left |
---|---|
1 | n/2 |
2 | n/4 |
3 | n/8 |
… | |
i | $n/2^i$ |
当比对次数足够多以后, 比对范围内就会仅剩余1个数据项,二分法查找的算法复杂度是O(log n)
- 虽然我们根据比对的次数, 得出二分查找的复杂度O(log n)
但本算法中除了比对, 还有一个因素需要
注意到:- binarySearch(alist[:midpoint],item)这个递归调用使用了列表切片,而切片操作的复
杂度是O(k),这样会使整个算法的时间复杂度稍有增加; - 当然,我们采用切片是为了程序可读性更好,实际上也可以不切片,而只是传入起始和结束的索引值即可,这样就不会有切片的时间开销了。
- binarySearch(alist[:midpoint],item)这个递归调用使用了列表切片,而切片操作的复
另外, 虽然二分查找在时间复杂度上优于顺序查找
但也要考虑到对数据项进行排序的开销
- 如果一次排序后可以进行多次查找,那么排序的开销就可以摊薄
- 但如果数据集经常变动,查找次数相对较少,那么可能还是直接用无序表加上顺序查找来得经济
所以, 在算法选择的问题上, 光看时间复杂度的优劣是不够的, 还需要考虑到实际应用的情况。