算法笔记-散列表3

为什么散列表和链表会经常一起使用

LRU缓存淘汰算法

总结链表那篇文章中,曾经实现过LRU缓存淘汰算法,但是当时的删除插入操作时间复杂是 O(n)。

如果用散列表加链表的方式实现LRU缓存淘汰算法,时间复杂度就变成O(1)。

数据的存贮我们用散列表实现,散列冲突用链表法解决。如果有相同散列值的元素,我们就将其插入到该散列值对应位置链表的尾部,链表上的节点都有一个 hnext 指针,指向该链表的后后继节点。

插入元素的排列顺序,我们用双向链表来维护。也就是说,散列表中的所有元素,除了因为散列冲突的原因,导致相同散列值元素之间有链表维护以外,还有一个双向链表将所有的元素串联起来。

查找某一元素的时候,根据散列值可以迅速定位元素位置,时间复杂度 O(1)。

删除某一元素的时候,查找的时间复杂度是 O(1),又因为该节点维护在双向链表中,所以删除操作时间复杂度也是 O(1),总的操作时间复杂度是 O(1)。

插入某一元素的时候,首先在散列表中查找该元素,若找到该元素则将其移动到双向链表的尾部,时间复杂度为 O(1),如果没有找到,则计算散列值存入散列表中,并将其维护到双向链表的尾部,时间复杂度为 O(1)。

上述操作中,隐含了一些条件,比如需要明确知道头结点,尾节点指针,这样才能保证插入双向链表尾部和头部的时间复杂度为 O(1)。

Redis有序集合

跳表那篇文章中总结到 Redis 的有序集合是通过跳表实现的,其实不然。

Redis 的有序集合中的元素有两个很重要的属性:key 和 score,我们不仅通过 key 查找数据还通过 score 查找数据。

举例:在有序集合中存储学生以及成绩信息,key 代表的是学生的学号,score 代表的是学生的成绩。那么对学生信息会有以下操作:
1:查找某个学生信息。
2:删除某个学生信息。
3:添加某个学生信息。
4:查找分数在某个区间的学生信息。
5:按照分数对学生排序。

如果单以跳表去存储维护学生分数信息,虽然保证了有序,但是查找学生信息的时间复杂度就是复杂了。

如果以学号为关键值用散列表存储学生的信息,查找时间复杂度为 O(1) 了,但是实现上述 4,5 的操作就复杂的多了。

所以近似于上述 LRU缓存淘汰算法 的数据结构存储学生信息,就能兼顾对学生信息的各种高效操作了,这就是 Redis 有序集合的实现原理。

Java LinkedHashMap

我在公司开发用到的主要语言就是 Java 其中的 LinkedHashMap 操作类经常用到,下面对其进行简单分析。

先看代码

HashMap<Integer, Integer> m = new LinkedHashMap<>();
m.put(3, 11);
m.put(1, 12);
m.put(5, 23);
m.put(2, 22);

for (Map.Entry e : m.entrySet()) {
  System.out.println(e.getKey());
}

输出结果是 3 1 5 2。首先从类名就看的出该操作类底层是 HashMap 实现的,但是输出元素居然是有序的,说明该结构中有除了用到散列表还用到了链表,用链表去维护数据的有序性。

再看代码:

// 10 是初始大小,0.75 是装载因子,true 是表示按照访问时间排序
HashMap<Integer, Integer> m = new LinkedHashMap<>(10, 0.75f, true);
m.put(3, 11);
m.put(1, 12);
m.put(5, 23);
m.put(2, 22);

m.put(3, 26);
m.get(5);

for (Map.Entry e : m.entrySet()) {
  System.out.println(e.getKey());
}

首先,当 m 分别放入 3,1,2,5这三个元素的时候,我们是知道这些数据是有序的,按照插入散列表中时间循序排列。当再次插入 key 值为 3 的元素的时候,LinkedHashMap 内部是先找到 key = 3 的元素,将其删除,然后插入新的 key = 3 的元素,并将其维护在链表的尾部。当访问 key = 5 的元素的时候,查找到该元素,并将其维护在链表的尾部。所以最终的输出结果是 1 2 3 5。

经过上述两段代码,我们可以发现 Java 中的 LinkedHashMap 其实实现的就是LRU缓存淘汰算法。

总结

其实,总结了这么多突然发现重要的其实就是两种数据结构:数组和链表。数组支持随机访问,但是插入删除不够高效,链表插入删除快,但是随机访问却低效。数组需要的是连续空间,对内存要求比较高,链表占用的是随机内存地址,提高了内存利用率。将数组与链表相互结合起来,取长补短,就能实现高性能的增删改查功能。

本文创作灵感来源于 极客时间 王争老师的《数据结构与算法之美》课程,通过课后反思以及借鉴各位学友的发言总结,现整理出自己的知识架构,以便日后温故知新,查漏补缺。

初入算法学习,必是步履蹒跚,一路磕磕绊绊跌跌撞撞。看不懂别慌,也别忙着总结,先读五遍文章先,无他,唯手熟尔~
与诸君共勉

关注本人公众号,第一时间获取最新文章发布,每日更新一篇技术文章。

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值