《编程导论》 CH10 一些简单的算法和数据结构
10.1 搜索算法
搜索算法就是在一个元素集合中寻找具有特殊属性的一组元素。把这个元素集合称为搜索空间。
本节分析两个用于搜索列表的算法,它们都满足如下需求:
def search(L,e):
"""假定L是列表
如果e在L中返回True,否则返回False"""
语法上和Python的表达式e in L相同。
1. 线性搜索和间接访问元素
def search(L,e):
for i in range(len(L)):
if L[i]==e:
return True
return False
在Python中,列表被表示为一个长度(表示列表中对象的数量)和一个固定大小的指针对象序列。
这是一种间接操作的实现技巧。使用间接操作来访问元素时会先访问另一个元素,其中包含对目标元素的引用。
访问列表中第i个元素所用的时间是常数,地址为start+4*i,算法复杂度O(len(L))
2.二分查找和利用假设
假设有一个包含整数的列表,其中的元素按升序存储。
def search(L,e):
for i in range(len(L)):
if L[i]==e:
return True
if L[i]>e:
return False
return False
搜索到大于e的数字就停止,这样可以降低平均运行时间,但是不会改变最坏情况下的复杂度。
def search(L,e):
def bSearch(L,e,low,high):
#Decrements high - low
if high==low:
return L[low]==e
mid = (low+high)//2
if L[mid]==e:
return True
elif L[mid]>e:
if low==mid: #finish
return False
else:
return bSearch(L,e,low,mid-1)
else:
return bSearch(L,e,mid+1,high)
if len(L)==0:
return False
else:
return bSearch(L,e,0,len(L)-1)
递归的二分查找
像search这样的函数通常称为包装函数。search函数会为用户代码提供一个优秀的接口,但是实际上并不执行真正的计算,而是用适当的参数调用辅助函数bSearch。
算法复杂度O(log(len(L)))
10.2 排序算法
利用二分查找需要先排序,如果需要搜索统一个列表多次(k次),那可以先对列表进行一次排序操作,当满足:
(sortComplexity(L)+k*log(len(L)))<k*len(L)时这是有价值的。
Python内建的排序算法足够高效O(nlog(n)): L.sort()对列表排序 sorted(L)返回的列表和L包含相同的元素而不修改L
选择排序:
选择排序会保持一个循环不变式:将列表分割成前一部分(L[0:i])和后一部分(L[i+1:len(L)]),前一部分是有序的,并且其中的所有元素都不大于后一部分中的最小元素。
def selSort(L):
"""assume L is a list,and elements could be compared by >,ascending sort"""
suffixStart =0
while suffixStart != len(L):
prefixEnd=suffixStart #书上没这句 编译不通过?
for i in range(prefixEnd,len(L)):
if L[i]<L[prefixEnd]:
L[prefixEnd],L[i]=L[i],L[prefixEnd]
suffixStart += 1
复杂度O(len(L)^2)
1.归并排序
前面说的二分查找是分治算法的一种。
分治算法可以被描述为:
1)一个输入大小的阈值,输入大小低于这个值的问题不会被分解;
2)子问题的大小和数量,据此对问题进行分解;