前言

缓存是一种提高数据读取性能的技术,在计算机中cpu和主内存之间读取数据存在差异,CPU和主内存之间有CPU缓存,而且在内存和硬盘有内存缓存。当主存容量远大于CPU缓存,或磁盘容量远大于主存时,哪些数据应该被应该被清理,哪些数据应该被保留,这就需要缓存淘汰策略来决定。常见的策略有三种:先进先出策略FIFO(First In,First Out)、最少使用策略LFU(Least Frequently Used)、最近最少使用策略LRU(Least Recently Used)。

LRU描述

设计和实现一个  LRU (最近最少使用) 缓存机制 。

实现 LRUCache 类:

  • LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
  • void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。

解题思路 哈希表 + 双向链表

  • 针对LRU的特点,选择使用双链表实现。
  • 使用 gut 方法获取数据,如果有数据,把返回数据,并且把数据放在链表头部。
  • 使用 put 方法存放数据,如果数据存在,直接覆盖新值;如果数据不存在,添加新值。新值都放在链表头部。此外,还需要判断缓存有没有超出容量 capacity,如果有超出,删除链表的尾结点。
  • 因为是单链表,每次获取数据,或者删除数据,都需要遍历一遍链表,时间复杂度是O(n),这里使用hash来记录每个数据的位置,将数据访问的时间复杂度降到O(1)。
class LRUCache {

    class DLinkedNode{
int key;
int value;
DLinkedNode prev;
DLinkedNode next; public DLinkedNode() {} public DLinkedNode(int key, int value) {
this.key = key;
this.value = value;
}
} private int size; private int capacity; private DLinkedNode head; private DLinkedNode tail; private Map<Integer,DLinkedNode> cache = new HashMap<>(); public LRUCache(int capacity) {
this.size = 0;
this.capacity = capacity;
head = new DLinkedNode();
tail = new DLinkedNode();
head.next = tail;
tail.prev = head;
} public int get(int key) {
DLinkedNode node = cache.get(key);
if (node == null) {
return -1;
}
//找到并移动到首位
moveToHead(node);
return node.value; } public void put(int key, int value) {
DLinkedNode node = cache.get(key);
if (node == null) {
//不存在就创建一个新的节点
DLinkedNode newNode = new DLinkedNode(key,value);
cache.put(key,newNode);
addToHead(newNode);
size++;
if (size > capacity) {
//超出容量,移除最后节点
DLinkedNode tail = removeTail();
cache.remove(tail.key);
size--;
}
} else {
//key存在,覆盖value,并移到头部
if (node.value != value) {
node.value = value;
}
moveToHead(node); }
} private DLinkedNode removeTail() {
DLinkedNode node = tail.prev;
removeNode(node);
return node;
} private DLinkedNode removeNode(DLinkedNode node) {
node.next.prev = node.prev;
node.prev.next = node.next;
return node;
} private void moveToHead(DLinkedNode node) {
removeNode(node);
addToHead(node);
} private void addToHead(DLinkedNode node) {
node.prev = head;
node.next = head.next;
head.next.prev = node;
head.next = node;
}
}

参考

LRU维基百科

极客时间-王争-如何实现LRU缓存淘汰算法?

最新文章

  1. 与谷歌测试工程师的对话 - from Google Testing Blog
  2. VS2012界面改为英文
  3. 实现C#给系统其他窗口输入的思路
  4. android 集成百度地图
  5. 史上最强php生成pdf文件,html转pdf文件方法
  6. Java编程入门(词汇表)
  7. 一则利用内核漏洞获取root权限的案例【转】
  8. Javascript 将字符串替换为特定的规律的字符串
  9. log4j.properties配置与将异常输出到Log日志文件实例
  10. SudokuGame 记软工第二次作业
  11. Linux下搭建ftp服务
  12. java线程大全一讲通
  13. 24.Mysql高级安装和升级
  14. python 回溯法 子集树模板 系列 —— 2、迷宫问题
  15. ArcEngine二次开发错误编码对照表(转)
  16. 区别js中name与id的简单方法
  17. css雪碧技术的用法。
  18. 图文例解C++类的多重继承与虚拟继承
  19. RabbitMQ 基础类和概念讲解
  20. 针对SQLServer数据库的通用访问类

热门文章

  1. python算法(2)兔子产子(斐波那切数列)
  2. QML用同一模版多开主窗口
  3. DC-6 靶机渗透测试
  4. Echarts 展示两条动态数据曲线
  5. 浅谈 SQL 注入(注入篇)
  6. 003 PCI Express体系结构(三)
  7. Windows Go 开发环境下载、安装并配置
  8. linux /etc/passwd详解
  9. redis的五大数据类型实现原理
  10. 【springboot】自动装配原理