1、列表
(1)内部机制:实现、冲突和散列函数
2、散列函数
(1)散列函数总是将相同的输入映射到相同的索引;将不同的输入映射到不同的索引。
(2)散列函数的数组长度是已知的,只返回有效的索引。
3、散列表(hash table)
(1)定义:结合散列函数和数组创建的一种数据结构,也称为散列映射、映射、字典、关联数组和字典。
(2)区别:数组和链表都被直接映射到内存,散列表是使用散列函数来确认元素的存储位置。
(3)组成:键、值,将键映射到值。【散列表很适用于模拟映射关系】
(4)散列表的查找、插入和删除速度很快;散列表是无序的,键值对的添加顺序无关紧要。
(5)平均情况下,散列表的各种操作时间为O(1),O(1)被称为常量时间,意味着不管散列表多大,所需的时间都是相同的。
最糟糕的情况,散列表所有操作的运行时间都为O(n)——线性时间。
因此,在平均情况下,散列表的查找(获取给定索引处的值)速度和数组是一样的,二插入和删除速度和链表是一样的,兼具了两者的优点。但在最糟糕的情况下,散列表的各种操作速度都是很慢的,故而需要避开最糟糕情况,需要避开冲突。
3、应用案例
(1)DNS解析(DNS resolution):网址硬是IP地址
(2)DEMO
# 创建一个字典,用大括号
dict={}
# 添加一些数据
dict["Ann"]=16
# 查找一些数据,get函数返回获取到的数据或者None
value=dict["Tom"]
(3)例子1
# 检查用户是否投过票
voted={}
def check_voter(name):
if voted.get(name):
print("Kick them out!")
else:
voted[name]=True
print("Let them vote")
check_voter("Tom")
check_voter("Milk")
check_voter("Milk")
(4)例子2
# Fackbook网址访问
cache={}
def get_page(url):
if cache.get(url):
# 返回缓冲的数据
return cache[url]
else:
data=get_data_from_server(url)
# 先将数据保存到缓存中
cache[url]=data
return data
(5)适用于:①模拟映射关系;②防止重复;③缓存/记住数据,以免服务器在通过处理来生成它们
4、冲突(collision)
(1)定义:给两个键分配相同位置
(2)解决方法:如果两个键映射到同一个位置,就在这个位置存储一个链表。
(3)散列表存储的链表很长,起散列表速度将下降。因此,使用散列函数很重要,使得链表不会很长,进而最大限度地减少冲突。
(4)避免冲突需要条件:①较低的填装因子;②良好的散列函数。
<1>填装因子=散列表包含的元素数/位置总数
=>填装因子度量的是散列表中多少位置是空的,填装因子大于1意味着超过数组的位置数,此时则需要调整长度(resizing),增加散列表的位置。
=>一旦填装因子大于0.7,就调整散列表的长度。平均而言,考虑到调整长度所需的时间,但列表操作所需的时间为O(1)。
=>填装因袭越低,发生冲突的可能性越小,散列表的性能越高。
<2>良好的散列函数:让数组中的值呈现均匀分布。【可研究于SHA函数,最后一章做简要介绍】
5.1 一致
5.2 不一致
5.3 不一致
5.4 一致
5.5 散列函数C和D可实现均匀分布
5.6 散列函数B和D可实现均匀分布
5.7 散列函数C和D可实现均匀分布
# B:会有重复的
print(len("Fun Home")) # 8
print(len("Watchmen")) # 8