搜索
搜索是从元素集合中找到某个特定元素的算法过程,结果通常是一个布尔值。
顺序搜索是一种最基本的搜索形式,他依次查找列表中的每个元素。要确定元素是否在列表中,就要依次将列表中每个元素进行比对,因此在最坏情况下需要比较列表的长度n次,最好的情况一开始就找到,比较一次,平均情况下比较n/2次。而在排序的情况下,效率将有所提高。
# 无序列表的搜索
def sequentialsearch(alist,item):
pos = 0
found = False
while pos < len(alist) and not found:
if item == alist[pos]:
found = True
else:
pos += 1
return found
# 有序列表的搜索
def ordersequentialsearch(alist,item):
pos = 0
found = False
stop = False
while pos < len(alist) and not found and not stop:
if item == alist[pos]:
found = True
else:
if alist[pos] < item:
stop = True
else:
pos += 1
return found
二分查找,在列表有序情况下,可以从中间的元素开始考虑,这样每次就能排除一般的元素。二分查找的时间复杂度是logn。但在n较少时,排序引起的额外开支可能是不必要的。
def binarysearch(alist,item):
first = 0
last = len(alist) - 1
found = False
while first <= last and not found:
midpoint = (first + last) // 2
if item == alist[midpoint]:
found = True
else:
if item < alist[midpoint]:
last = midpoint - 1
else:
first = midpoint + 1
return found
# 二分查找的递归
def binarysearchpro(alist,item):
if len(alist) == 0:
return False
else:
midpoint = len(alist) // 2
if item == alist[midpoint]:
return True
else:
if item < alist[midpoint]:
return binarysearchpro(alist[:midpoint],item)
else:
return binarysearchpro(alist[midpoint+1:],item)
散列表是一个元素集合,他的每个位置是一个可以存储元素的槽,槽用一个0开始的元素标记,以下是一个有11个槽的散列表。
散列函数将散列表元素与其位置对应起来,对散列表元素,散列表函数返回0到m-1的元素
使用余数做散列值,而散列的占用率称载荷因子。
在搜索目标时,仅需要计算出槽编号并查看对应值。不过他需要元素间的散列值不同。完美散列函数指的是每个元素能映射不同的槽。扩展元素的方法折叠法、平方取中法。 折叠法是将元素平均切分求和,再计算其元素。平方取中法,再从中间取几位。
处理冲突
开放定址法法+线性探查:发生冲突后遍历散列寻找空的地址存放元素。再散列是发生冲突后寻找另一个槽的过程,同时可以通过跨步大小使得元素的分布更加平均。平方探测就是在限定探测的基础上,用平方扩展跨步的大小。
链接法,在同一个位置存储多个元素。
字典可以利用键来查找对应的值。
class HashTable:
def __init__(self):
self.size = 11 #散列表初始值
self.slots = [None] * self.size #储存栈
self.data = [None] * self.size #储存值
def put(self,key,data):
hashvalue = self.hashfunction(key,len(self.slots))
if self.slots[hashvalue] == None:
self.slots[hashvalue] = key
self.data[hashvalue] = data
else:
if self.slots[hashvalue] == key:
self.data[hashvalue] = data #替换
else:
nextslots = self.rehash(hashvalue,len(self.slots))
while self.slots[nextslots] != None and self.slots[nextslots] != key:
nextslots = self.rehash(nextslots,len(self.slots))
if self.slots[nextslots] == None:
self.slots[nextslots] = key
self.data[nextslots] = data
else:
self.data[nextslots] = data
def hashfunction(self,key,size):
return key*size
def rehash(self,oldhash,size):
return (oldhash+1) % size
用字典实现,字典可以用栈来查找,这种关系被称为映射。这个散列表的初始大小为11,在处理冲突时采用+1的散列函数的线性探测法。在put方法中,先计算哈希值用来做寻找槽的散列值,如果槽不存在键,就存入键和值,如果键存在且相同,就用新的值代替旧的值。除此以外,继续寻找一个槽来重复以上过程,新的散列值为nextslot。
而在get方法中,如果查找值不在初始散列值对应的槽中,就寻找下一个值,直到再次循环到初始散列值时,若仍未找到则停止程序。
class Hashtable:
def __init__(self):
self.size = 11
self.slot = [None] * self.size
self.data = [None] * self.size
def put(self,key,data):
hashvalue = self.hashfunction(key,len(self.slot))
if self.slot[hashvalue] == None:
self.slot[hashvalue] = key
self.data[hashvalue] = data
else:
if self.slot[hashvalue] == key:
self.data[hashvalue] = data
else:
nextslot = self.reshash(hashvalue,len(self.slot))
while self.slot[nextslot] != None and self.slot[nextslot] != key:
nextslot = self.reshash(hashvalue, len(self.slot))
if self.slot[nextslot] == None:
self.slot[nextslot] = key
self.data[nextslot] = data
else:
self.data[nextslot] = data
def get(self,key):
startslot = self.hashfunction(key,len(self.slot))
found = False
stop = False
data = None
pos = startslot
while self.slot[pos] != None and not stop and not found:
if self.slot[pos] == key:
found = True
data = self.data[pos]
else:
pos = self.reshash(pos,len(self.slot))
if pos == startslot:
stop = True
return data
def hashfunction(self,key,size):
return key % size
def reshash(self,oldhash,size):
return (oldhash + 1) % size
def __getitem__(self, key):
return self.get(key)
def __setitem__(self, key, data):
self.put(key,data)