4.11散列表

散列表上

1.抛出一个思考题开课

word中单词拼写检查功能如何实现的?

学完今天的内容你就微软office工程师一样,轻松实现这个功能

2.散列表

散列表用的就是数组支持按照下标[随机访问]数据的特性,实现时间复杂度o(1),所以散列表其实就是数组的一种扩展。

3.散列冲突

我们常用的散列冲突解决方法有两类,开放寻址法(open addressing)和链表法(chaining)。

i.开放寻址法

线性探测(+1)

二次探测(+1^2)

双重散列(再次散列)

用装载因子来观测空位多少,装载因子越大,说明空闲位置越少,冲突越多,散列表的性能会下降。

ii.链表法

在散列表中,每个“桶(bucket)”或者“槽(slot)”会对应一条链表,所有散列值相同的元素都放到相同槽位对应的链表中。

3.开篇解答

散列表存储单词字典,然后散列查询,查不到可能拼写错误

常用的英文单词有 20 万个左右,假设单词的平均长度是 10 个字母,平均一个单词占用 10 个字节的内存空间,那 20 万英文单词大约占 2MB 的存储空间,就算放大 10 倍也就是 20MB。对于现在的计算机来说,这个大小完全可以放在内存里面。所以我们可以用散列表来存储整个英文单词词典。

4.小结

散列表两个核心问题是散列函数设计和散列冲突解决。散列冲突有两种常用的解决方法,开放寻址法和链表法。

5.课后思考题

假设我们有 10 万条 URL 访问日志,如何按照访问次数给 URL 排序?

解:url为key,访问次数为value,10万*100B=20MB

有两个字符串数组,每个数组大约有 10 万条字符串,如何快速找出两个数组中相同的字符串?

解:一个字符串做参考串做key,出现次数为value,第二个字符串做检验串,遍历字符串查key,若value不为0则存在相同

散列表中

1.散列表碰撞攻击

在极端情况下,恶意攻击者,通过精心构造的数据,使得所有的数都散列到同一个槽。如使用的是基于链表的冲突解决方法,散列表就会退化为链表,查询的时间复杂度就从 O(1) 急剧退化为 O(n)。这样就有可能因为查询操作消耗大量 CPU 或者线程资源,导致系统无法响应其他请求,从而达到拒绝服务攻击(DoS)的目的。这就是散列表碰撞攻击的基本原理(调虎离山)

2.如何设计工业级散列函数

工业级散列函数标准:

i.支持快速的查询、插入、删除操作;

ii.内存占用合理,不能浪费过多的内存空间;

iii.性能稳定,极端情况下,性能不会退到无法接受

如何实现这样一个散列表:

i.设计一个合适的散列函数;

ii.定义装载因子阈值,并且设计动态扩容策略;

iii.选择合适的散列冲突解决方法。

3.简单的随机函数

i.数据分析法

选择越随机的做key,可能重复的做value如下:

运动员编号做key其他信息做value

手机后四位做key前面各位做value

ii.进位相加

比如单词拼写检查,每位26个,

hash("nice")=(("n" - "a") * 26*26*26 + ("i" - "a")*26*26 + ("c" - "a")*26+ ("e"-"a")) / 78978

4.装载因子过大

静态数据集,容易设计散列函数,因为数据都是已知的

动态数据集,数据集合频繁变动,无法预估,有扩大和缩小两种

假设变大,数据集慢慢变大,装载因子也会变大,直到崩溃,借助前面所学的动态扩容,可以解决

i.数组动态扩容

假设原来装载因子0.8,现在扩容为原来2倍,装载因子变为0.4,然后数据搬移

ii.避免低效扩容

将新数据插入新散列表,并且从老的散列表中拿出一个数据放入到新散列表,一点点分批完成插入,查询就改为先查老表再查新表

假设变小,数据集慢慢变小,装载因子变小,如果内存吃紧,可以设置阀值动态缩容

5.冲突解决办法

数据量小、装载因子小,适合采用开放寻址法。这也是 Java 中的ThreadLocalMap使用开放寻址法解决散列冲突的原因。

大对象,大数据量,适合基于链表的散列冲突处理方法,比起开放寻址法,它更加灵活,支持更多的优化策略,比如用红黑树代替链表,比如,Java 中 LinkedHashMap 就采用了链表法解决冲突。

6.工业级散列表:java中HashMap

i.初试大小

HashMap 默认的初始大小是 16,如果事先知道大概数据量多大,可修改默认初始大小,减少动态扩容的次数,这会提高HashMap 的性能。

ii.装载因子和动态扩容

最大装载因子默认是 0.75,当 HashMap 中元素个数超过 0.75*capacity(capacity 表示散列表的容量)的时候,就会启动扩容,每次扩容都会扩容为原来的两倍大小。

iii.散列冲突解决办法

HashMap 底层采用链表法来解决冲突,免不了会出现拉链过长的情况,会严重影响 HashMap 的性能。在 JDK1.8 版本,引入了红黑树。而当链表长度太长(默认超过 8)时,链表就转换为红黑树。可以利用红黑树快速增删改查的特点,提高 HashMap 的性能。当红黑树结点个数少于 8 个的时候,又会将红黑树转化为链表。因为在数据量较小的情况下,红黑树要维护平衡,比起链表来,性能上的优势并不明显。

iiii.散列函数设计

int hash(Object key) {

    int h = key.hashCode();

    return (h ^ (h >>> 16)) & (capitity -1);

//capicity 表示散列表的大小

}

public int hashCode() {

  int var1 = this.hash;

  if(var1 == 0 && this.value.length > 0) {

    char[] var2 = this.value;

    for(int var3 = 0; var3 < this.value.length; ++var3) {

      var1 = 31 * var1 + var2[var3];

    }

    this.hash = var1;

  }

  return var1;

}

散列表下

1.为何散列表和链表经常一起使用

链表实现插入删除o(1),散列表实现查询o(1)

比如LRU,跳表,LinkedHashMap

2.LRU缓存淘汰算法

在链表那一节中,借助散列表,可以把 LRU 缓存淘汰算法的时间复杂度降低为 O(1)。

思想:first in last out,一个链表,查询找到就放到末尾,查询不到插入也放到末尾(表示是最新加入的元素放最后删除),如果满存了,就删除头部元素

分析:三个操作:插入,删除,查询,最关键是查询o(n)如何让查询变为o(1),建立新的散列表(索引)来专门实现查询,原链表实现插入删除

实现:用双向链表存储数据,链表中7的每个结点处理存储数据(data)、前驱指针(prev)、后继指针(next)之外,还新增了一个特殊的字段 hnext,用来将结点串入到索引表链表中

注:这时每次结点都在两条链表中,一是双向链表中,二是索引表链表中

3.Redis有序集合

在有序集合中,每个成员对象有两个重要的属性,key(键值)和score(分值)。我们不仅会通过 score 来查找数据,还会通过 key 来查找数据。

i.添加一个成员对象;

ii.按照键值来删除一个成员对象;

iii.按照键值来查找一个成员对象;

iiii.按照分值区间查找数据,比如查找积分在 [100, 356] 之间的成员对象;

iiiii.按照分值从小到大排序成员变量;

解:i.ii.iii可以借助双向链表和索引表之链表法两个链表实现,iiii.iiiii可以用跳表实现,若要一起实现五个功能,肯定是两者合一,组成一个新的数据结构实现

4.Java中LinkedHashMap

LinkedHashMap 也是通过散列表和链表组合在一起实现的。实际上,它不仅支持按照插入顺序遍历数据,还支持按照访问顺序来遍历数据。

如:put(3,2)(1,10)(5,7)(2,9)

解:遍历结果是:3-1-5-7

说明是按照插入顺序遍历的的

如:put(3,2)(1,10)(5,7)(2,9)(3,25)get(5)

解:遍历结果是1-2-3-5

说明LinkedHashMap效果跟LRU一模一样,说明两者原理同,双向链表和散列表两表一起实现

注:LinkedHashMap 本身就是一个支持 LRU 缓存淘汰策略的缓存系统,Linked表示双向链表,HashMap表示链表法解决冲突,两者合并使用

5.课后思考

题一,为何使用双向链表不是单向链表?

因为单链表插入删除需分别定位到后一个位置,前一个位置,在操作,需要o(n),故使用双向链表

题二,10个对象,内容包括id和积分,需要实现

i.根据id快速查找,删除,插入积分信息

ii.积分在某个区间的所有id

iii.积分从小到大排序,查找任意区间[x,y]所有id

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352

推荐阅读更多精彩内容