数据结构与算法笔记day17:散列表(下)

        在前面的学习中,我们发现散列表经常会和链表放在一起使用,这是为什么呢?

        这节课我们就结合几个例子来看看为什么~       

    1LRU缓存淘汰算法

        LRU,顾名思义,Least Rencently Used,最近最少使用。

        借助散列表,我们可以把LRU缓存淘汰算法的时间复杂度降低为O(1),现在我们来看看它是怎么做到的~

        首先回顾一下当时我们是如何通过链表来实现LRU缓存淘汰算法的。

        我们需要维护一个按照访问时间从大到小有序排列的链表结构,因为缓存大小有限,当缓存空间不够的时候,需要淘汰一个数据,我们就直接将链表头部的结点删除。

        当要缓存某个数据的时候,先在链表中查找这个数据。如果没有找到,则直接将数据放到链表尾部;如果找到了,就把它移动到链表尾部。

        因为查找数据需要遍历链表,所以单纯用链表实现的LRU缓存淘汰算法的时间复杂度很高,是O(n)。

        一个缓存(cache)系统主要包含下面这几种操作:

        往缓存中添加一个数据;

        从缓存中删除一个数据;

        从缓存中查找一个数据。

        这三个操作都要涉及“查找”操作。如果我们将散列表和链表两种数据结构组合使用,就可以将这三个操作的时间复杂度都降低到O(1),具体的结构如下图所示:

        由上图我们可以看到,链表中的每个结点除了存储数据(data)、前驱指针(prev)、后继指针(next)外,还新增了一个特殊的字段hnext。

        因为散列表是通过链表法解决散列冲突的,所以每个结点会在两条链中。一个链是双向链表,另一个链是散列表中的拉链。前驱和后继指针是为了将结点串在双向链表中,hnext指针是为了将结点存在散列表的拉链中。

        OK,散列表和双向链表的组合结构就是这样的,我们再来看它是如何做到时间复杂度是O(1)的。

        首先,查找数据。散列表中查找数据的时间复杂度接近O(1),通过散列表,我们可以很快地在缓存中找到一个数据。当找到数据之后,我们还需要将它移动到双向链表的尾部。

        其次,删除数据。我们通过O(1)的时间复杂度找到要删除的结点,因为链表是双向的,所以可以通过前驱指针获取前驱节点并删除,这个操作的时间复杂度是O(1)。因此,双向链表中删除结点只需要O(1)的时间复杂度。

        最后,添加数据。首先需要查看这个数据是否已经存在在缓存中,若存在,则将该结点移动到链表尾部;若不存在,则看缓存有没有满,若没有满,则插在链表尾部,若满了,则将头结点删除并将新结点插入尾部。而查找、删除、插入的操作,都可以在O(1)时间复杂度内完成。所以添加数据的时间复杂度是O(1)。

        至此,我们就通过散列表和双向链表的组合使用,实现了一个高效的、支持LRU缓存淘汰算法的缓存系统原型。

    2Redis有序集合

        在跳表那一节我们对有序其实作了一些简化,实际上,有序集合中的每个成员对象有两个重要属性,key(键值)和score(分值)。我们不仅会通过score来查找数据,还会通过key来查找数据。

        如果我们仅仅按照分值将成员对象组织成跳表的结构,那按照键值来删除、查询成员对象就会很慢,解决方法与LRU缓存淘汰算法的解决方法类似。我们可以再按照键值构建一个散列表,这样按照key来删除、查找一个成员对象的时间复杂度就变成了O(1)。同时,借助跳表结构,其他操作也非常有效。

    3Java LinkedHashMap

        Java LinkedHashMap是Java中的一种容器,HashMap底层是通过散列表这种数据结构实现的,而LinkedHashMap比HashMap多了一个“Linked”。

        其实,“Linked”也并不仅仅代表它是通过链表解决散列冲突的。

        我们先来看一段代码:

        这段代码的打印顺序会是什么样的呢?

        答案是:3,1,5,2

        但是会有一点比较奇怪的是,散列表中的数据是经过散列函数打乱之后无规律存储的,这里是如何实现按照数据的插入顺序来遍历打印的呢?

        其实,LinkedHashMap也是通过散列表和链表组合在一起实现的,实际上,它不仅支持按照插入顺序遍历数据,还支持按照访问顺序来遍历数据,可以看一下下面这段代码:

        它的打印结果是1,2,3,5。

        因为,每次调用put()函数,往LinkedHashMap中添加数据的时候,都会将数据添加到链表的尾部,所以,在前四个操作完成之后,链表中的数据是下面这样:

        第8行代码中,再次将键值为3的数据放入到LinkedHashMap的时候,会先查找这个键值是否已经有了,然后,再将(3,11)删除,并将新的(3,26)放到链表的尾部。所以,这个时候链表中的数据就是下面这样:

        当第9行代码访问到key为5的数据的时候,我们将被访问到的数据移动到链表的尾部。所以,第9行代码之后,链表中数据的顺序又变成了这样:

        因此,最后打印出来的顺序是1,2,3,5。

        其实呀,访问按照时间排序的LinkedHashMap本身就是一个支持LRU缓存淘汰策略的缓存系统,实际上它们的实现原理也是一模一样滴。

        总结一下,LinkedHashMap是通过双向链表和散列表这两种数据结构组合实现的,其中的“Linked”实际上是指的双向链表,并非指用链表法解决散列冲突。

    4散列表&双向链表|内容小结

        散列表这种数据结构虽然支持非常高效的数据插入、删除、查找操作,但是散列表中的数据都是通过散列函数打乱之后无规律存储的,也就是说,它无法支持按照某种顺序快速地遍历数据。如果希望按照顺序遍历散列表中的数据,我们就需要将散列表中的数据拷贝到数组中,然后排序,再遍历。

        因为散列表是动态的数据结构,会不停地有数据的插入、删除,所以每当我们希望按顺序遍历散列表中的数据的时候,都需要先排序,效率就会很低。所以解决这个问题的最佳方法就是将散列表和链表(或者)跳表结合在一起使用。

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

推荐阅读更多精彩内容