在我们内存或者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相关推荐

  1. 内存管理-LRU,NFU算法实现

    LRU和NFU的算法实现 (1)LRU算法实现 LRU,汤子瀛<计算机操作系统>译为"最近最久未使用",也即在缓冲的所有页面中,缺页中断发生时,将最久未被使用的页面置换 ...

  2. 实现 LRU 缓存机制

    实现 LRU 缓存机制 文章目录 实现 LRU 缓存机制 一.什么是 LRU 算法 二.LRU 算法描述 三.LRU 算法设计 四.代码实现 一.什么是 LRU 算法 LRU 就是一种缓存淘汰策略.( ...

  3. Linux 内存管理。

    首先我先说明最近才真的用心整理和查看linux内存管理方面知识,之所以写这篇博客只是为了整理. fightting 参考书籍 Linux 内核源代码情景分析 深入理解Linux虚拟内存管理 首先还是上 ...

  4. lru调度算法例题_lru算法(lru算法及例题讲解)

    lru算法参考例子如下: include using namespace std; int ans[1000]://存放原序列 int block[1000]://机器分给程序的内存块 int num ...

  5. linux系统堆、栈及内存分配、CPU寄存器

    堆和栈: 栈主要用来存放局部变量, 传递参数, 存放函数的返回地址.esp 始终指向栈顶, 栈中的数据越多, esp的值越小. 堆用于存放动态分配的对象, 当你使用 malloc , new 等进行分 ...

  6. linux环境内存分配原理

    Linux的虚拟内存管理有几个关键概念: Linux 虚拟地址空间如何分布?malloc和free是如何分配和释放内存?如何查看堆内内存的碎片情况?既然堆内内存brk和sbrk不能直接释放,为什么不全 ...

  7. linux监控平台搭建-内存

    上一篇文章说的硬盘.就写一下.更加重要的东西.在手机上面是RAM.机器是memory.内存是按照字节编址.每个地址的存储单元可以存放8bit的数据.cpu 通过内存地址获取一条指令和数据.内存溢出ou ...

  8. 想不到!面试官问我:Redis 内存满了怎么办?

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 来源:http://rrd.me/et29e Redis占用内存大 ...

  9. Linux 下判断Server 内存是否不足

    2019独角兽企业重金招聘Python工程师标准>>> 下面我来写一下在linux OS 下如何判断系统是否内存吃紧. 我从两个部分来查看如何检查. 第一部分:查看内存使用状况 Li ...

最新文章

  1. 找出占用磁盘空间最大的前10个文件或文件夹
  2. 【爬虫、算法】基于Dijkstra算法的武汉地铁路径规划!
  3. 测试小白入门必知必会的8个测试工具
  4. 开始抽时间学习swift
  5. UVA 1609 Foul Play 不公平竞赛 (构(luan)造(gao)+递归)
  6. 《Python Cookbook 3rd》笔记(4.10):序列上索引值迭代
  7. 云优后台提交显示parsererror_微信现场大屏实时显示结果现场投票活动制作方法...
  8. 解决ERROR 1396 (HY000): Operation ALTER USER failed for root@localhost
  9. rsync(一):基本命令和用法
  10. python之matplotlib中plt.show()不显示
  11. 什么是Ruby on Rails?
  12. Chrome不保留历史记录,常规模式下不保留历史记录
  13. XCode9插件安装,XCode9.2插件安装,XCode8和XCode9的Alcatraz安装
  14. 用笔记本创建wifi热点
  15. 游戏测试好还是软件测试好,浅学软件测试 软件测试和游戏测试哪个有前途?...
  16. 解决数据库数据粘贴到excel中换行、换列问题
  17. Python一个回合制兵棋小游戏(1)
  18. MYSQL复制表结构及数据到新表
  19. 成绩忽好忽坏的孩子,往往缺乏良好的学习习惯--一语中的
  20. 计算机如何寻找ppt文件,电脑上没保存的PPT怎么找回来

热门文章

  1. 深度图怎么看行情走势
  2. TreeSet的使用方法总结、实现原理、使用示例
  3. ng Baidu Baidu Baidu ang
  4. 塔石E18D mqtt连接onenet
  5. PHP扩展 -- pecl, pear, composer
  6. 昨天,我终于见到了传说中的牛叉架构老炮儿...
  7. 解决:Short read of DER length
  8. linux内核空间内存动态申请
  9. 咕咕机G3vs喵喵机使用心得对比
  10. 非盈利组织能力建设探讨