散列函数
散列函数必须满足的性质:
- 一致性:一个x的总是相同的
- 单射
散列函数指出了价格的储存位置,这样是因为:
- 散列函数总是将同样的输入映射到相同的索引
- 散列函数将不同的输入映射到不同的索引
- 散列函数知道数组大小,只返回有效索引
Python中散列表的实现——字典,使用dict
创建散列表
book=dict()
book['apple']=0.67
book['milk']=1.49
book['avocado']=1.49
print(book)
>>>{'apple': 0.67, 'milk': 1.49, 'avocado': 1.49}
print(book['apple'])
>>>0.67
应用案例
查找电话号码
防止重复
假设我管理一个投票站,每个人只能投一票,为了防止有人重复投票,我可以使用散列表检查这个人是否已经投票。
voted={}
value=voted.get("tom")
如果"tom"在散列表中,函数get
将返回它;否则返回None.我们可以使用这个函数检查投票的人是否投过票。
voted={}
def check_voter(name):
if voted.get(name):
print('给爷爬')
else:
voted[name]=True
print('来吧来吧')
check_voter('tom')
>>>来吧来吧
check_voter('lily')
>>>来吧来吧
check_voter('tom')
>>>给爷爬
将散列表用作缓存
缓存的简单原理:网站将数据记住,而不再重新计算
- 向网站服务器发送请求
- 服务器做些处理,生成一个网页并将其发送过来
- 获得一个网页
缓存的优点:
- 用户能更快的看到网页
- 网站需要做的工作更少
缓存将页面url映射到页面数据
cache={}
def get_page(url):
if cache.get(url):
return cache[url]
else:
data=get_data_from_server(url)
cache[url]=data
return data
小结
散列表用于:
- 模拟映射关系
- 防止重复
- 缓存/记住数据,以免服务器再通过处理来生成它们
冲突
冲突(collision):给两个键分配的位置相同。
处理冲突的方式:如果两个键储存到同一个位置,则在这个位置存储一个链表
如果出现所有键都存储在同一个位置,那这个散列表也不好。我们得到两点经验教训:
- 散列函数最好将键均匀地映射到散列表的不同位置
- 散列函数的链表不应该太长
性能
散列表的平均情况速度为
O
(
1
)
O(1)
O(1)
最糟情况速度为
O
(
n
)
O(n)
O(n):全部存在一个链表里,为此我们要避免冲突:
- 较低的填装因子
- 良好的散列函数
填装因子
填装因子=
n
m
\frac{n}{m}
mn
n为散列表包含的元素数,m为位置总数。一旦填装因子开始增大,就需要在散列表中添加位置,这被称为调整长度。(一旦填装因子大于0.7,就调整散列表的长度。
良好的散列函数——SHA函数
小结
- 为了尽量避免冲突,我们应使用减少冲突的散列函数
- 散列表的查找、插入和删除速度都非常快。(怎么删除呢)
- 一旦填装因子超过0.7,就该调整散列表的长度。
- 散列表可用于缓存数据(例如,在Web服务器上)。
- 散列表非常适合用于防止重复。