Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and set.

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
set(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.

  • 题目大意:为LRU Cache设计一个数据结构,它支持两个操作:

   1)get(key):如果key在cache中,则返回对应的value值,否则返回-1

   2)set(key,value):如果key不在cache中,则将该(key,value)插入cache中(注意,如果cache已满,则必须把最近最久未使用的元素从cache中删除);如果key在cache中,则重置value的值。

  • 解题思路:题目让设计一个LRU Cache,即根据LRU算法设计一个缓存。在这之前需要弄清楚LRU算法的核心思想,LRU全称是Least

Recently Used,即最近最久未使用的意思。在操作系统的内存管理中,有一类很重要的算法就是内存页面置换算法(包括FIFO,LRU,LFU等几种常见页面置换算法)。事实上,Cache算法和内存页面置换算法的核心思想是一样的:都是在给定一个限定大小的空间的前提下,设计一个原则如何来更新和访问其中的元素。下面说一下LRU算法的核心思想,LRU算法的设计原则是:如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。也就是说,当限定的空间已存满数据时,应当把最久没有被访问到的数据淘汰。

  而用什么数据结构来实现LRU算法呢?可能大多数人都会想到:用一个数组来存储数据,给每一个数据项标记一个访问时间戳,每次插入新数据项的时候,先把数组中存在的数据项的时间戳自增,并将新数据项的时间戳置为0并插入到数组中。每次访问数组中的数据项的时候,将被访问的数据项的时间戳置为0。当数组空间已满时,将时间戳最大的数据项淘汰。

  这种实现思路很简单,但是有什么缺陷呢?需要不停地维护数据项的访问时间戳,另外,在插入数据、删除数据以及访问数据时,时间复杂度都是O(n)。

  那么有没有更好的实现办法呢?

  那就是利用链表和hashmap。当需要插入新的数据项的时候,如果新数据项在链表中存在(一般称为命中),则把该节点移到链表头部,如果不存在,则新建一个节点,放到链表头部,若缓存满了,则把链表最后一个节点删除即可。在访问数据的时候,如果数据项在链表中存在,则把该节点移到链表头部,否则返回-1。这样一来在链表尾部的节点就是最近最久未访问的数据项。

  总结一下:根据题目的要求,LRU Cache具备的操作:

  1)set(key,value):如果key在hashmap中存在,则先重置对应的value值,然后获取对应的节点cur,将cur节点从链表删除,并移动到链表的头部;若果key在hashmap不存在,则新建一个节点,并将节点放到链表的头部。当Cache存满的时候,将链表最后一个节点删除即可。

  2)get(key):如果key在hashmap中存在,则把对应的节点放到链表头部,并返回对应的value值;如果不存在,则返回-1。

  1. package com.Netesay.interview;
  2.  
  3. import java.util.HashMap;
  4. import java.util.Map;
  5.  
  6. /**
  7. * @Author: weblee
  8. * @Email: likaiweb@163.com
  9. * @Blog: http://www.cnblogs.com/lkzf/
  10. * @Time: 2014年10月24日下午6:29:40
  11. *
  12. ************* function description ***************
  13. *
  14. ****************************************************
  15. */
  16.  
  17. public class LRUCache {
  18. Map<Integer, CacheNode> cacheMap;
  19. CacheNode head, tail;
  20. int capacity;
  21.  
  22. //使用双向链表和map,map将k对应与链表的节点
  23. //链表里保存k和value
  24. public LRUCache(int capacity) {
  25. this.capacity = capacity;
  26.  
  27. cacheMap = new HashMap<Integer, CacheNode>(capacity);
  28.  
  29. head = new CacheNode(-1, -1);
  30. tail = new CacheNode(1, 1);
  31.  
  32. head.next = tail;
  33. tail.pre = head;
  34. }
  35.  
  36. public int get(int key) {
  37. if (cacheMap.containsKey(key)) {
  38. CacheNode node = (CacheNode)cacheMap.get(key);
  39.  
  40. put2Head(node);
  41.  
  42. return node.value;
  43. } else {
  44. return -1;
  45. }
  46. }
  47.  
  48. public void set(int key, int value) {
  49. if (cacheMap.containsKey(key)) {
  50. CacheNode p = cacheMap.get(key);
  51.  
  52. p.value = value;
  53.  
  54. put2Head(p);
  55. } else if(cacheMap.size() < capacity) {
  56. CacheNode node = new CacheNode(key, value);
  57. put2Head(node);
  58. cacheMap.put(key, node);
  59. } else {
  60. CacheNode p = new CacheNode(key, value);
  61. put2Head(p);
  62. cacheMap.put(key, p);
  63.  
  64. int tmpKey = removeEnd();
  65. cacheMap.remove(tmpKey);
  66. }
  67. }
  68.  
  69. private void put2Head(CacheNode p) {
  70. if (p.next != null && p.pre != null) {
  71. p.pre.next = p.next;
  72. p.next.pre = p.pre;
  73. }
  74.  
  75. p.pre = head;
  76. p.next = head.next;
  77. head.next.pre = p;
  78. head.next = p;
  79. }
  80.  
  81. private int removeEnd() {
  82. CacheNode p = tail.pre;
  83. tail.pre.pre.next = tail;
  84. tail.pre = p.pre;
  85.  
  86. p.pre = null;
  87. p.next = null;
  88.  
  89. return p.key;
  90. }
  91. }
  92.  
  93. class CacheNode {
  94. int key;
  95. int value;
  96.  
  97. CacheNode pre;
  98. CacheNode next;
  99.  
  100. public CacheNode(int key, int value) {
  101. this.key = key;
  102. this.value = value;
  103. }
  104. }

面试题:实现LRUCache::Least Recently Used的缩写,意思是最近最少使用,它是一种Cache替换算法的更多相关文章

  1. lettcode 上的几道哈希表与链表组合的数据结构题

    目录 LRU缓存 LFU缓存 全O(1)的数据结构 lettcode 上的几道哈希表与链表组合的数据结构题 下面这几道题都要求在O(1)时间内完成每种操作. LRU缓存 LRU是Least Recen ...

  2. 操作系统之LRU算法 C语言链表实现

    LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰.该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历 ...

  3. 听说同学你搞不懂Java的LinkedHashMap,可笑

    先看再点赞,给自己一点思考的时间,微信搜索[沉默王二]关注这个有颜值却假装靠才华苟且的程序员.本文 GitHub github.com/itwanger 已收录,里面还有我精心为你准备的一线大厂面试题 ...

  4. 【python刷题】LRU

    什么是LRU? LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰.该算法赋予每个页面一个访问字段,用来记录一个页面自上次 ...

  5. JS 实现一个 LRU 算法

    LRU 是 Least Recently Used 的缩写,即最近最少使用,是一种常用的页面置换算法,选择内存中最近最久未使用的页面予以淘汰. 可用的 NodeJS 库见node-lru-cache ...

  6. Javascript 手写 LRU 算法

    LRU 是 Least Recently Used 的缩写,即最近最少使用.作为一种经典的缓存策略,它的基本思想是长期不被使用的数据,在未来被用到的几率也不大,所以当新的数据进来时我们可以优先把这些数 ...

  7. 什么是LRU缓存淘汰机制

    LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰.该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历 ...

  8. Android——LruCache源码解析

    以下针对 Android API 26 版本的源码进行分析. 在了解LruCache之前,最好对LinkedHashMap有初步的了解,LruCache的实现主要借助LinkedHashMap.Lin ...

  9. 常见面试题之操作系统中的LRU缓存机制实现

    LRU缓存机制,全称Least Recently Used,字面意思就是最近最少使用,是一种缓存淘汰策略.换句话说,LRU机制就是认为最近使用的数据是有用的,很久没用过的数据是无用的,当内存满了就优先 ...

随机推荐

  1. node.js在windows下的学习笔记(2)---简单熟悉一些命令

    1.打开如下的安装 2.输入node -v,显示node的版本号 3.输入node --help.显示帮助命令 4.在命令行中输入node,按下回车键,当出现>符号的时候即进入了node的REP ...

  2. Redis学习手册(Set数据类型)

    一.概述: 在Redis中,我们可以将Set类型看作为没有排序的字符集合,和List类型一样,我们也可以在该类型的数据值上执行添加.删除或判断某一元素是否存在等操作.需要说明的是,这些操作的时间复杂度 ...

  3. bindiff 4.2使用

    要求IDA 6.8 程序1与程序2混合比较图 2---------------------------------------------------------------------------- ...

  4. 总结:调用startActivityForResult,onActivityResult无响应的问题

    人人都知道,可以通过使用 startActivityForResult() 和 onActivityResult() 方法来传递或接收参数. 但你是否遭遇过onActivityResult()不执行或 ...

  5. FOR XML PATH 转换问题

    以下我带大家了解关于 FOR XML PATH 首先我们看下所熟悉的表数据 之后转换 <骨牌编号>1</骨牌编号> <骨牌颜色>橙</骨牌颜色> < ...

  6. js读取Excel文件数据-IE浏览器

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head ...

  7. c语言学习之基础知识点介绍(十九):内存操作函数

    一.malloc函数 /* 首先需要导入头文件 #include <stdlib.h> malloc void* malloc(n); n是字节大小 开辟堆空间,开辟的字节数以n为准 返回 ...

  8. Android colors.xml

    <?xml version="1.0" encoding="utf-8"?><resources> <color name=&qu ...

  9. Java—static、this、super用法总结

        通过用static来定义方法或成员,为我们编程提供了某种便利,从某种程度上可以说它类似于C语言中的全局函数和全局变量.(理解为加了static的就是全局变量)但是,并不是说有了这种便利,你便可 ...

  10. Solr4.7新建core

    Solr里面的core就像数据库里面的一个表,用来管理索引和相关配置. 一. 使用示例core 下载的solr完整包里面solr-4.7.0\example\multicore这个文件夹下面有2个示例 ...