主要内容:
什么是列表查找
顺序查找
二分查找
一、什么是查找?
(1)查找:在一些数据元素中,通过一定的方法找出与给定关键字相同的数据元素的过程。
(2)列表查找(线性表查找):从列表中查找指定元素。
a.输入:列表、待查找的元素;
b.输出:元素下标(未找到元素时一般返回None或者-1)
(3)内置列表查找函数:index()
二、顺序查找(Linear Search)
1、定义
顺序查找,也叫线性查找,从列表第一个元素开始,顺序进行搜索,直到找到或搜索到列表最后一个元素为止。
2、顺序查找代码
(1)使用enumerate()函数获得列表list的索引。
enumerate()函数,多用于在for循环中得到计数,利用它可以同时获得索引和值。
# 顺序查找,时间复杂度O(n)
def linear_search(li,val):
for ind, v in enumerate(li): #使用enumerate函数,输出索引和值
if v == val: #当当前值等于所需要的值
return ind #返回索引
else:
return None #针对最后一个if的else,最后一个if仍旧不成立,则输出None
lis = [1,6,7,8,56,7,0,23,5]
lin = linear_search(lis,0)
print(lin)
#输出结果
6
(2)使用range函数和len函数读取list的索引。
#顺序查找——不用enumerate()函数
def lin_search(data, value):
for i in range(len(data)): #len()得到列表的长度,range()函数生成连续整数
if data[i] == value:
return i
else:
return None
li = lin_search(lis,0)
print(li)
#输出结果
6
3、二分查找介绍
(1)定义
二分查找又叫折半查找,从有序列表的初始候选区li[0:n]开始,通过对待查找的值与候选区中间值的比较,可以使候选区减少一般。
(2)实例介绍
例如在有序列表中查找元素3:
第一步:
定义两个变量,left 和right,表示当前列表的索引(即位置)。首先,left表示索引0,right表示索引n-1,那么中间值索引为(0+n-1)/2。
第二步:
第一个mid中间索引为8/2=4, 索引4对应的值为5。且5大于3,因此,查找的候选区更新为5的左侧列表。
第三步:
right所代表的索引值,更改为mid所代表索引值的左侧,即mid的索引值-1,即4-1=3。
第四步:
重新更新mid代表的索引值,同样根据公式计算,(0+3)/2=1.5,向下取整得到1。即mid的索引值变成1,对应的列表数值为2。
第五步:
重复之前的操作,对比列表值2小于3,则候选区更新为2的右侧,left代表的索引值移动到mid+1的位置,即索引值为1+1=2。候选区索引更新为[2,3]。
第六步:
重新计算mid代表的索引,(2+3)/2=2.5,向下取整为2。mid索引对应的列表值为3,与所查找的值一致。
4、二分查找代码
代码的时间复杂度为O(logn)。
def binary_search(li,val): #li是列表,val是需要查找的值
left = 0 #left初次索引
right = len(li) - 1 #right的初次索引
while left <= right:
mid = (left + right) // 2 #向下取整
if li[mid] == val:
return mid
elif li[mid] > val: #待查值在mid的左侧
right = mid - 1 #更新right的值
else: #li[mid] < val 待查值在mid右侧
left = mid + 1
else: #对应最后一个if
return None
#验证代码
lis = [1,2,3,4,5,6,7,8]
bin = binary_search(lis,3) #待查值为3
print(bin)
#输出结果
2
5、二分查找与线性查找的比较
(1)时间复杂度
线性查找(顺序查找):O(n); 二分查找:O(logn)
因此,二分查找的速度大于顺序查找。
1)时间装饰器
装饰器的原理认识,可参考:http://c.biancheng.net/view/2270.html
带参数的时间装饰器代码如下:
import time #调用时间模块
#时间装饰器
def cal_time(func):
def wrapper(*args, **kwargs): #函数参数不确定的时候,用*args和**kwargs,前者叫位置参数,后者叫关键字参数。
t1 = time.time()
result = func(*args, **kwargs) #运行被装饰的函数
t2 = time.time()
print("%s running time: %s secs." % (func.__name__,t2-t1)) #func.__name__装饰器的函数,表示函数名称
return result
return wrapper
2)同数据下线性查找和二分查找运行时间代码
from cal_time import cal_time
# 线性查找
@cal_time
def linear_search(li,val):
for ind, v in enumerate(li): #使用enumerate函数,输出索引和值
if v == val: #当当前值等于所需要的值
return ind #返回索引
else:
return None
#二分查找
@cal_time
def binary_search(li,val):
left = 0
right = len(li) - 1
while left <= right:
mid = (left + right) // 2
if li[mid] == val:
return mid
elif li[mid] > val:
right = mid - 1
else:
left = mid + 1
else:
return None
lis = list(range(10000000))
binary_search(lis,389000)
linear_search(lis,389000)
3)输出时间对比
binary_search running time: 0.0 secs.
linear_search running time: 0.03690075874328613 secs.
对比可以看出,确实是二分查找的时间更快。
(2)使用范围
1)python自带的函数index(),使用的就是顺序查找。可以发现,顺序查找可以任何情况,一般没有顺序的列表使用线性查找(顺序查找)。
2)二分查找一般用于顺序列表,若顺序是乱的,就需要先排序后使用二分查找,然而,排序的时间复杂度往往很高,将会导致所需时间比顺序查找更慢。
3)当某个无顺序的列表,需要多次查找的情况,可先给列表排序后使用二分查找,时间效率较高。