---用教授的方式学习。
目录
10.1 搜索算法
本节会研究两种搜索列表的算法,每种方法都满足以下规范:
def search(L, e):
"""假设L是列表
如果e是L中的元素,则返回True,否则返回False"""
10.1.1 线性搜索与间接引用元素
Python使用以下算法确定列表中是否有某个元素:
for i in range(len(L)):
if L[i] == e:
return True
return False
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:
return False
return False
这种算法可以缩短平均运行时间,但不会改变最差情形下的算法复杂度,因为在最差情形下还是需要检查L中的每个元素。
二分查找的思路非常简单:
(1) 选择一个可以将列表L大致一分为二的索引i;
(2) 检查是否有L[i] == e;
(3) 如果不是,检查L[i]大于还是小于e;
(4) 根据上一步的结果,确定在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)
10.2 排序算法
从一个简单但是低效的算法开始:选择排序。
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
10.2.1 归并排序
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)
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-6中的代码,会输出以下结果:
Sorted by last name = ['Tom Brady', 'Gisele Bundchen', 'Eric Grimson'] Sorted by first name = ['Eric Grimson', 'Gisele Bundchen', 'Tom Brady'] |
10.3 散列表
如果字典中只包含10 000(104 )个人员的条目,就会浪费大量空间。
为了解决这个问题,我们引入散列函数。它会将一个大规模的输入空间(如所有自然数)映射为一个小规模的输出空间(如0~5000的自然数)。所以,可以使用散列函数将数量巨大的键转换为数量较少的整数索引。
我们使用一个简单的散列函数
class intDict(object):
"""键为整数的字典"""
def __init__(self, numBuckets):
"""创建一个空字典"""
self.buckets = []
self.numBuckets = numBuckets
for i in range(numBuckets):
self.buckets.append([])
def addEntry(self, key, dictVal):
"""假设key是整数。添加一个字典条目。"""
hashBucket = self.buckets[key%self.numBuckets]
for i in range(len(hashBucket)):
if hashBucket[i][0] == key:
hashBucket[i] = (key, dictVal)
return
hashBucket.append((key, dictVal))
def getValue(self, key):
"""假设key是整数。
返回键为key的字典值"""
hashBucket = self.buckets[key%self.numBuckets]
for e in hashBucket:
if e[0] == key:
return e[1]
return None
def __str__(self):
result = '{'
for b in self.buckets:
for e in b:
result = result + str(e[0]) + ':' + str(e[1]) + ','
return result[:-1] + '}' #result[:-1] omits the last comma
上述实现的基本思想是通过散列桶列表表示intDict类的实例,每个散列桶都是一个元组形
式的键/值对列表。通过这种每个桶都是一个列表的方式,我们可以将散列到同一个桶的所有值
都保存在列表里,从而解决碰撞问题。
-----end