内存替换算法——LRU
在我们内存或者cache中有两种替换算法来保证内存或者cache中都是“热点”的数据,一个是LRU和LFU。这里面们先介绍LRU,在这里的get和set操作的都是O(1),因为这两种速度都是很快的,不能要求O(n)。
【问题】
设计一种缓存结构,该结构在构造时确定大小,假设大小为K,并有两个功能:
set(key, value):将记录(key, value)插入该结构。
get(key):返回key对应的value值。
【要求】
1.set和get方法的时间复杂度为O(1)。
2.某个key的set或get操作一旦发生,认为这个key的记录成了最经常使用的。
3.当缓存的大小超过K时,移除最不经常使用的记录,即set或get最久远的。
【举例】
假设缓存结构的实例是cache,大小为3,并依次发生如下行为:
1.cache.set("A", 1)。最经常使用的记录为("A", 1)。
2.cache.set("B", 2)。最经常使用的记录为("B", 2),("A", 1)变为最不经常的。
3.cache.set("C", 3)。最经常使用的记录为("C", 3),("A", 1)还是最不经常的。
4.cache.get("A")。最经常使用的记录为("A", 1),("B", 2)变为最不经常的。
5.cache.set("D", 4)。大小超过了3,所以移除此时最不经常使用的记录("B", 2),
加入记录("D", 4),并且为最经常使用的记录,然后("C", 3)变为最不经常使用的记录。
【题解】
储存数据用hash_map,因为hash表的增删该查的复杂度都为O(1)
本来使用频率使用队列存储,使用的放在头,不使用的自动向后排,最不经常使用的在队尾
但每次从队列中取出正在使用的数据至队列头部的复杂度为O(N),不满足条件。所以只能使用双向链表来进行存储,hash表中存放着每个数据节点的地址参数,故链表的中数据位置调整复杂度为O(1)。最经常使用的数据节点在链表的尾部,不经常使用的在链表的尾部,因为缓存数据时需要经常使用的,而链表的尾部插入更加方便。
【代码】
1.基础数据结构Node(节点)
public static class Node<K, V> {public K key;public V value;public Node<K, V> last;public Node<K, V> next;public Node(K key, V value) {this.key = key;this.value = value;}}
2.基础数据结构NodeDoubleLinkedList(双向链表)
addNode方法表示在双向链表中从尾部添加一个新的节点。
moveNodeToTail方法使用前必须保证传入的Node节点在双向链表中,该方法表示把双向链表中的节点放在链表最后的操作。
removeHead方法表示删除双向链表的头结点。
public static class NodeDoubleLinkedList<K, V> {private Node<K, V> head;private Node<K, V> tail;public NodeDoubleLinkedList() {head = null;tail = null;}public void addNode(Node<K, V> newNode) {if (newNode == null) {return;}if (head == null) {head = newNode;tail = newNode;} else {tail.next = newNode;newNode.last = tail;tail = newNode;}}public void moveNodeToTail(Node<K, V> node) {if (this.tail == node) {return;}if (this.head == node) {this.head = node.next;this.head.last = null;} else {node.last.next = node.next;node.next.last = node.last;}node.last = this.tail;node.next = null;this.tail.next = node;this.tail = node;}public Node<K, V> removeHead() {if (this.head == null) {return null;}Node<K, V> res = this.head;if (this.head == this.tail) {this.head = null;this.tail = null;} else {this.head = res.next;res.next = null;this.head.last = null;}return res;}}
3.基础数据结构MyCache(自定义缓存)
MyCache构造方法,自定义的缓存大小是固定不变的。定义一个HashMap结构keyNodeMap
用于查看K是否之前就有,V表示Node节点。再定义一个双向链表nodeList。
get方法表示如果HashMap结构中之前存在这个K,就把这个K所对应的节点在双向链表中的位置放在最后。
removeMostUnusedCache方法表示移除双向链表的头节点以及HashMap中的对应关系。
set方法表示如果之前的HashMap结构存储过这个对应关系,就修改这个节点的值,并且把这个节点在双向链表中的位置放在最后。如果之前的HashMap结构没有存储过这个对应关系,并且当前缓存容量未超过设定的值,则新建Node节点并且在HashMap结构和双向链表中添加对应关系。缓存容量超过设定值则使用 removeMostUnusedCache方法删除双向链表的头节点。
public static class MyCache<K, V> {private HashMap<K, Node<K, V>> keyNodeMap;private NodeDoubleLinkedList<K, V> nodeList;private final int capacity;public MyCache(int cap) {if (cap < 1) {throw new RuntimeException("should be more than 0.");}keyNodeMap = new HashMap<K, Node<K, V>>();nodeList = new NodeDoubleLinkedList<K, V>();capacity = cap;}public V get(K key) {if (keyNodeMap.containsKey(key)) {Node<K, V> res = keyNodeMap.get(key);nodeList.moveNodeToTail(res);return res.value;}return null;}public void set(K key, V value) {if (keyNodeMap.containsKey(key)) {Node<K, V> node = keyNodeMap.get(key);node.value = value;nodeList.moveNodeToTail(node);} else {if (keyNodeMap.size() == capacity) {removeMostUnusedCache();}Node<K, V> newNode = new Node<K, V>(key, value);keyNodeMap.put(key, newNode);nodeList.addNode(newNode);}}private void removeMostUnusedCache() {Node<K, V> removeNode = nodeList.removeHead();keyNodeMap.remove(removeNode.key);}}
内存替换算法——LRU相关推荐
- 内存管理-LRU,NFU算法实现
LRU和NFU的算法实现 (1)LRU算法实现 LRU,汤子瀛<计算机操作系统>译为"最近最久未使用",也即在缓冲的所有页面中,缺页中断发生时,将最久未被使用的页面置换 ...
- 实现 LRU 缓存机制
实现 LRU 缓存机制 文章目录 实现 LRU 缓存机制 一.什么是 LRU 算法 二.LRU 算法描述 三.LRU 算法设计 四.代码实现 一.什么是 LRU 算法 LRU 就是一种缓存淘汰策略.( ...
- Linux 内存管理。
首先我先说明最近才真的用心整理和查看linux内存管理方面知识,之所以写这篇博客只是为了整理. fightting 参考书籍 Linux 内核源代码情景分析 深入理解Linux虚拟内存管理 首先还是上 ...
- lru调度算法例题_lru算法(lru算法及例题讲解)
lru算法参考例子如下: include using namespace std; int ans[1000]://存放原序列 int block[1000]://机器分给程序的内存块 int num ...
- linux系统堆、栈及内存分配、CPU寄存器
堆和栈: 栈主要用来存放局部变量, 传递参数, 存放函数的返回地址.esp 始终指向栈顶, 栈中的数据越多, esp的值越小. 堆用于存放动态分配的对象, 当你使用 malloc , new 等进行分 ...
- linux环境内存分配原理
Linux的虚拟内存管理有几个关键概念: Linux 虚拟地址空间如何分布?malloc和free是如何分配和释放内存?如何查看堆内内存的碎片情况?既然堆内内存brk和sbrk不能直接释放,为什么不全 ...
- linux监控平台搭建-内存
上一篇文章说的硬盘.就写一下.更加重要的东西.在手机上面是RAM.机器是memory.内存是按照字节编址.每个地址的存储单元可以存放8bit的数据.cpu 通过内存地址获取一条指令和数据.内存溢出ou ...
- 想不到!面试官问我:Redis 内存满了怎么办?
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 来源:http://rrd.me/et29e Redis占用内存大 ...
- Linux 下判断Server 内存是否不足
2019独角兽企业重金招聘Python工程师标准>>> 下面我来写一下在linux OS 下如何判断系统是否内存吃紧. 我从两个部分来查看如何检查. 第一部分:查看内存使用状况 Li ...
最新文章
- 找出占用磁盘空间最大的前10个文件或文件夹
- 【爬虫、算法】基于Dijkstra算法的武汉地铁路径规划!
- 测试小白入门必知必会的8个测试工具
- 开始抽时间学习swift
- UVA 1609 Foul Play 不公平竞赛 (构(luan)造(gao)+递归)
- 《Python Cookbook 3rd》笔记(4.10):序列上索引值迭代
- 云优后台提交显示parsererror_微信现场大屏实时显示结果现场投票活动制作方法...
- 解决ERROR 1396 (HY000): Operation ALTER USER failed for root@localhost
- rsync(一):基本命令和用法
- python之matplotlib中plt.show()不显示
- 什么是Ruby on Rails?
- Chrome不保留历史记录,常规模式下不保留历史记录
- XCode9插件安装,XCode9.2插件安装,XCode8和XCode9的Alcatraz安装
- 用笔记本创建wifi热点
- 游戏测试好还是软件测试好,浅学软件测试 软件测试和游戏测试哪个有前途?...
- 解决数据库数据粘贴到excel中换行、换列问题
- Python一个回合制兵棋小游戏(1)
- MYSQL复制表结构及数据到新表
- 成绩忽好忽坏的孩子,往往缺乏良好的学习习惯--一语中的
- 计算机如何寻找ppt文件,电脑上没保存的PPT怎么找回来