typora-copy-images-to: Python 编程导论
文章目录
Python 编程导论 Chapter 10 —— 一些简单算法和数据结构
-
本章目的:
- 建立某种通用直觉,以解决算法效率方面的问题
-
程序效率的关键是好的算法,我们要做的是,学会在面对问题时将复杂性减到最低,并将他们转化为已经解决的问题
10.1 搜索算法
- 搜索算法就是在一个项目集合中找出一个或一组具有某种特点的项目
# 本节研究两种搜索列表的方法
def search(L, e):
"""假设L是列表
如果e是L中的元素,则返回True,否则返回False"""
语义上和python表达式 e in L 完全相同
10.1.1 线性搜索与间接引用元素
for i in range(len(L)):
if L[i] == e:
return True
return False
最差结果,算法执行O(len(L))次测试
- 计算机的间接引用:要访问目标元素时,先访问另一个元素,再通过包含在这个元素中的引用来访问目标元素
10.1.2 二分查找和利用假设
- 假设我们对元素的存储顺序有所了解?
例如,假设我们知道一个整数列表是按照升序存储元素的,那么可以修改函数实现,搜索到一个大于目标整数的数值时,就停止搜索
def search(L, e):
"""假设L是列表,其中元素按升序排列。
ascending order.
如果e是L中的元素,则返回True,否则返回False"""
for i in range(len(L)):
if L[i] == e:
return True
if L[i] > e: 比如L = [1, 2, 4, 5] e = 3 的时候
return False
return False
# 以上算法可以缩短平均运行时间,但是不改变复杂度
-
使用二分查找的算法可以有效改善最差情形下的复杂度
-
二分查找的思路:
- 选择一个可以将列表L大致一分为二的索引
- 是否有L[i] == e
- 如果不是,检查L[i]大于还是小于e
- 根据上一步结果,确定在L 的左半部还是右半部分搜索e
-
给定算法结构之后,实现二分查找的最简单直接的方式就是使用递归
# 递归二分查找
def search(L, e):
"""假设L是列表,其中元素按升序排列。
ascending order.
如果e是L中的元素,则返回True,否则返回False"""
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: #nothing left to search
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这样的函数经常被称作为包装器函数,为什么会有辅助函数searchb的产生,因为参数high、low与搜索这一抽象任务无关,因此需要隐藏
- searchb的复杂度仅仅依赖于递归调用的次数
- search函数的复杂度为O(log(len(L)))
10.2 排序算法
- 是否意味着在有列表搜索需求时,应该先排序再执行搜索?
- 不可以,因为排序首先就是需要对列表的每个元素检查一次
- 但是当我们要对目标列表多次搜索时,性价比就会提高
- 使用python的内置函数sorted,生成一个排序后的列表,不对L修改
# 选择排序
def selSort(L):
"""假设L是列表,其中的元素可以用>进行比较。
compared using >.
对L进行升序排列"""
suffixStart = 0
while suffixStart != len(L):
#检查后缀集合中的每个元素
for i in range(suffixStart, len(L)):
if L[i] < L[suffixStart]:
#交换元素位置
L[suffixStart], L[i] = L[i], L[suffixStart]
suffixStart += 1
- 该函数的复杂度为L长度的平方,非常低效
10.02.1 归并排序
-
分治算法的基本思想是,先找出出事问题的一些简单实例的解,再组合起来,作为整体解分治算法的特征:
- 一个输入规模的阈值,低于这个阈值的问题不会进行分解
- 一个实例分解成子实例的规模和数量
- 合并子解的算法
-
阈值有时候成为递归基
-
归并排序是一种典型的分治算法
- 如果列表的长度是0或者1,那么它已经排好序了
- 如果包含多于1个元素,分成两个列表,并分别使用归并排序法进行排序
- 合并结果
假设要合并列表[1, 5, 12, 18, 19, 20] 和 [2, 3, 4, 17]:
列表1中剩余元素 | 列表2中剩余元素 | 目标列表 |
[1,5,12,18,19,20] [2,3,4,17] []
[5,12,18,19,20] [2,3,4,17] [1]
[5,12,18,19,20] [3,4,17] [1,2]合并两个有序列表的复杂度与列表的长度成线性关系
def merge(left, right, compare):
"""假设left和right是两个有序列表,compare定义了一种元素排序规则。
返回一个新的有序列表(按照compare定义的顺序),其中包含与
(left+right)相同的元素。"""
result = []
i,j = 0, 0
while i < len(left) and j < len(right):
if compare(left[i], right[j]):
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
while (i < len(left)):
result.append(left[i])
i += 1
while (j < len(right)):
result.append(right[j])
j += 1
return result
def mergeSort(L, compare = lambda x, y: x < y):
"""假设L是列表,compare定义了L中元素的排序规则
on elements of L
返回一个新的具有L中相同元素的有序列表。"""
if len(L) < 2:
return L[:]
else:
middle = len(L)//2
left = mergeSort(L[:middle], compare)
right = mergeSort(L[middle:], compare)
return merge(left, right, compare)
L = [2, 1, 4, 5, 3]
print(mergeSort(L),mergeSort(L, lambda x, y: x > y))
10.2.2 将函数用作参数
def lastNameFirstName(name1, name2):
arg1 = name1.split(' ')
arg2 = name2.split(' ')
if arg1[1] != arg2[1]:
return arg1[1] < arg2[1]
else: #姓相同,则按照名排序
return arg1[0] < arg2[0]
def firstNameLastName(name1, name2):
arg1 = name1.split(' ')
arg2 = name2.split(' ')
if arg1[0] != arg2[0]:
return arg1[0] < arg2[0]
else: #名相同,则按照姓排序
return arg1[1] < arg2[1]
L = ['Tom Brady', 'Eric Grimson', 'Gisele Bundchen']
newL = mergeSort(L, lastNameFirstName)
print('Sorted by last name =', newL)
newL = mergeSort(L, firstNameLastName)
print('Sorted by first name =', newL)
# 将函数用作了参数
10.2.3 Python 中的排序
L = [3,5,2]
D = {'a':12, 'c':5, 'b':'dog'}
print(sorted(L))
print(L)
L.sort()
print(L)
print(sorted(D))
D.sort()
# 输出
[2, 3, 5]
[3, 5, 2]
[2, 3, 5]
['a', 'b', 'c']
AttributeError: 'dict' object has no attribute 'sort'
-
sorted 应用于字典时,会返回排好序的字典键的列表
-
list.sort 方法和 sorted 函数都可以有两个附加参数。参数 key 的作用和我们实现归并排序
时的 compare 一样:提供用于排序的比较函数,参数 reverse 指定对列表进行升序还是降序排序