为何散列表会出现
- 场景:若是超市买东西结帐的时候,售货员在一个本子里查找价格
- 若是是有序的(二分查找)还好,时间是 O(logn),若是是无序(简单查找)的话,那就是 O(n)
- 若是直接有一个 O(1)的查找速度就行了
散列函数
将输入映射到数字数组
- 一致性:输入 apple 时获得的是 4,那么每次输入 apple 都必须是 4
- 不一样性:不一样的输入将获得不一样的输出。若是输入 apple 和 yoki 都获得 4,那么这是一个很差的散列函数。
创造散列表
结合散列函数和数组浏览器
- 建立一个空数组
- apple 输入到散列函数,输出 3,而后把 apple 的价格存储到数组索引 3
- 接着各个类推,知道存完数组
- 而后咱们要找 apple 的价格,就把 apple 输入交给散列函数,获得 3 去数组里面找
散列表的经典应用
查找,防止重复,用于缓存缓存
查找
- 被用于大海捞针的查找
- 这个就不用细说了,相似的有 dns 解析
防止重复
场景:负责一个投票站,每一个人只能投一票,如何避免重复投票服务器
- 有一个方法是:有人来投票,就将它记录在一个投票名单里面,而后接下来的人都遍历这个投票名单,若是有就不能投票,可是这样就列表会愈来愈长,就会变慢
- 另一种就是散列表,超快
缓存
- 浏览器缓存的数据存储在散列表里面
- 若是访问 facebook 的页面,会先检查散列表中是否存储了该页面
- 若是不在缓存中,才去访问服务器,而后把数据放到缓存里,这样下次有人访问就能够直接命中缓存
冲突
事实上不可能不一样的输入都能得到不一样的值app
- 假设有一个数组包含 26 个位置,而咱们使用的散列函数很是简单,按照字母表顺序分配位置
- apple 第一个,bear 第二个,接下来来了一个 banana,理应分到第二个,可是这个时候第二个位置已是 bear 了
- 这个就叫冲突,解决冲突的办法有不少,简单的就是在这个冲突的位置存储一个链表,bear 末尾的指针指向 banana
- 因此散列函数很重要,好的散列函数将 key 均匀的映射到散列表的不一样位置
如何避免冲突
- 较低的填装因子
- 散列表包含的元素数:位置总数就是填装因子
- 越小越好,满了只能调整长度,换一个更长的数组
- 一个不错的经验规则就是,一旦填装因子大于 0.7,那么就要调整散列表的长度
- 良好的散列函数
散列表性能如何
平均状况
最糟糕状况
为何会有两种状况,这是由于有了冲突可能会靠链表解决,n 个长度的链表,最好状况下没有冲突,就常量时间 )函数