学习编程知识的同时,梳理知识,也便于以后查找
tags: 图解算法、B站视频
散列表
最有用的基本数据结构之一。第一种学到的包含额外逻辑的数据结构。
查找时间O(1)!!!
加密散列函数如彩虹表
散列函数
散列函数的要求
- 必须一致。一个输入对应的输出是固定的
- 不同的输入映射到不同的数字。
第一种学到的包含额外逻辑的数据结构。散列表使用散列函数来确定元素的存储位置。
散列表也被称为散列映射、映射、字典和关联数组。
散列表也使用数组来存储数据,因此其获取元素的速度和数组一样快。
创建散列表
散列表由键和值组成。
对于同样的输入,散列表必须返回同样的输出
。这一点很重要。
"""散列表"""
# 创建散列表
book = dict()
# 添加数据
book["张三0"] = 99
book["张三1"] = 9
book["张三2"] = 44
print(book)
print(book['张三1'])
一些散列函数示例
# 无论输入什么都返回1
f(x) = 1
# 无论输入什么都返回一个随机数
f(x) = rand()
# 返回散列表中下一个空位置的索引
f(x) = next)empty_solt()
# 将字符串长度作为索引
f(x) = len(x)
散列表应用案例
散列表用途广泛。
- 电话簿
- 网址转换为IP地址(DNS解析)
防止重复
# 投票案例
voted = {}
# 检查在不在散列表中
value = voted.get("Tom")
if value:
print("已经投过票!")
else:
voted("Tom") = True
将散列表用作缓存
为提高网站访问速度,往往使用缓存技术。
缓存是一种常用的加速技术,所有大型网站都使用缓存。
缓存的优点
- 用户能更快的看到网页
- 网站需要做的工作更少
缓存的数据存储在散列表中。
代码实现
cache = {}
def get_page(url):
if cache.get(url):
return cache[url]
else:
data = get_data_from_server(url)
#将数据保存到缓存中
cache[url] = data
return data
冲突
大多数语言都提供了散列表实现。基础时可以先不考虑散列表的内部原理,只需考虑散列表的性能问题。
冲突:给两个键分配的位置相同(即计算得出的hash值相同)
解决冲突
最简单的办法
如果两个键映射到了同一个位置,就在这个位置存储一个链表。
这里的经验教训有两个
- 散列函数很重要。最理想的情况是,散列函数将键均匀地映射到散列表的不同位置
- 如果散列表存储的链表很长,散列表的速度会急剧下降。如果使用的散列函数很好,这些链表就不会很长!
常用的三种方法 链表法、再哈希法、
- 链表法 在传统位置,添加链表,通过链表存储元素。缺点是链表查找效率低。也是java中HashMap在基本情况下的使用方法
- 开放定址法 若是发生哈希冲突,就以当前地址为基准,根据再寻址的方法(探查序列),去寻找下一个地址,若再冲突再去寻找,直至找到为止
- 再哈希法 换用其它的哈希函数
性能
在平均情况下,散列表执行各种操作的时间都为O(1).(常量时间)
在最糟情况下,散列表所有操作的运行时间都为O(n)–线性时间。
在使用散列表时,避开最糟情况至关重要。为此,需要避免冲突,避免冲突,需要有
- 较低的装填因子
- 良好的散列函数
装填因子
散列表的装填因子很容易计算
散列表包含的元素数 / 位置总数
散列表使用数组来存储数据,因此需要计算数组中被占用的位置数。
装填因子的度量是散列表中有多少位置是空的
装填因子大于1,意味着元素数量超过了数组的位置数。
一旦装填因子开始增大,就需要在散列表中添加位置,这被称为调整长度
。
装填因子越低,散列表的性能越高。
一个不错的经验规则是:一旦装填因子大于0.7,就调整散列表的长度。
调整长度的开销很大,要避免频繁地调整长度。但平均而言,即便考虑到调整长度所需的时间,散列表操作所需的时间也为O(1).
散列函数
良好的散列函数让数组中的值呈均匀分布。
可以使用SHA函数,作为散列函数。
–END–