IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 17 散列表+链表的使用场景 -> 正文阅读

[数据结构与算法]17 散列表+链表的使用场景

LRU缓存算法

LRU思想:维护一个按时间从旧到新排序的链表结构,因为缓存大小有限,当缓存空间不足时,需要淘汰一个数据,此时直接删除头节点,时间复杂度O(1)。当要缓存数据时,需要先查找数据,如果没有查找到,则放到链表尾部,因为查找需要遍历链表时间复杂度是O(n),所以单纯用链表做LRU缓存算法的时间复杂度也是O(n),很高。
总结一下:
LRU算法需要三个功能:
1、往缓存空间添加数据
2、从缓存空间删除数据
3、从缓存空间查找数据
三个操作都涉及查找,所以单纯用链表的话,时间复杂度很高。但是散列表的查找时间复杂度是O(1),所以散列表+链表实现LRU的话,可以将三个功能操作的时间复杂度降到O(1)。
在这里插入图片描述
如图:
每个结点有
data:存储数据
prev:前驱指针,纵向指针,维护数据缓存的时间线
next:后继指针,纵向指针,维护数据缓存的时间线
hnext:横向指针,维护解决散列表的散列冲突的单链表。
next和hnext有什么区别呢,prev和next维护的是双向链表,按照时间缓存连接。
而hext维护的是散列表每个槽内的单链表。

散列表+链表实现LRU:
1、查找功能:散列表查找数据时间复杂度是O(1),查找到数据后,通过修改prev和next将数据移动到双向链表的尾部,但是hnext
2、删除功能:需要查找数据,然后删除。查找借助散列表查找数据,时间复杂度是O(1)。因为是双向链表,可以通过前驱指针和后继指针删除结点只要O(1)
3、添加功能:需要先看数据是否在缓存中,如果已经存在,需要移其道双向链表尾部;不存在的话,需要看缓存是否已满,满的话删除头部结点,然后把数据放到尾部,不满的话直接放到双向链表尾部。
总结:散列表+链表实现LRU,三个操作都是O(1),实现了一个高效的、支持LRU的缓存算法的存储系统原型。

有序集合

有序集合中有两个重要的属性,key键值和score分值,功能有:
1、添加成员对象
2、根据键值删除成员对象
3、根据键值查询成员对象
4、根据分值查找数据,例如查[100-356]的数据
5、根据分值从大到小排序数据。
如果只按照分值来将成员对象创建成跳表,那这样要实现删除、查找功能的时间复杂度是很高的。所以也是像LRU一样,根据key创建一个散列表,通过散列表实现查找、删除功能,时间复杂度是O(1),然后根据跳表实现对分值的排序以及查找。

JAVA LinkedHashMap

LinkedHashMap比HashMap多了一个linked,这并不是表示用linked链表解决散列冲突,而是表示双链表。实际上LinkedHashMap是通过双链表+散列表组合而成,原理与双链表+散列表的LRU缓存算法一致的。
例如:

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,正常散列表的数据是打乱后无规则存储的,但是这里是按插入顺序遍历打印的。实际上不仅可以按顺序遍历打印,还能按访问顺序遍历数据,与LRU一致。

// 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());
}

打印结果是1,2,3,5.
具体分析,4-7行代码后,数据添加到链表,链表呈现如下:
在这里插入图片描述

第9行代码后,将key=3的键值放入链表中,因为key=3在链表以及存在了,所以删除原来的(3,11),重新在链表尾部添加(3,26)
在这里插入图片描述
第10行代码后,查询key=9的数据,被查询的数据移动到尾部表示最新访问的。
在这里插入图片描述

所以输出1,2,3,5
总结:LinkedHashMap与基于散列表+双向链表的LRU缓存算法原理一致,
为什么散列表总是跟链表一起使用
1、散列表的查找、插入、删除是很高效的,但是数据都是打乱无规律存储的,所以它无法支持按照某种顺序快速遍历数据。
2、链表或者跳表可以高效排序遍历列表的数据,所以二者需要结合使用。

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-10-22 21:39:02  更:2022-10-22 21:41:45 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 20:14:16-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码