题目
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
LRU是Least Recent Used的缩写,即最少使用的Cache置换算法,是为虚拟页式存储管理服务的。
Cache是高速缓存,这是IT行业经常见到的概念。CPU中的Cache能极大提高存取指令和数据的时间,让整个存储器(Cache+内存)既有Cache的高速度,又有内存的大容量。
Cache虽然速度快,但是容量有限。因此当Cache容量用完而又有新的内容添加进来的时候,就需要选取Cache中的部分内容进行舍弃,然后添加新的内容。LRU Cache的替换规则是:每次选取最久不被使用的内容进行舍弃,然后添加新的内容。之前上操作系统,老师告诉我的LRU Cache Algorithm中文翻译是最近最久未使用算法。
思路
LRU的典型实现是double linked list + hash map。
双向链表根据每个节点最近被访问的时间有序存储,最近被访问的节点存储在表头,最近没有被访问的节点存储的表尾,存储依据是因为:最近被访问的节点在接下来的一段时间仍有很大的概率被再次访问到。
哈希表的作用是用来提高查找效率,如果不使用哈希表,则查找一个节点的时间复杂度是O(n),而使用了哈希表,则每个节点的查找时间复杂度为O(1)。
查找(GET)操作:
- 根据键值查找hashmap,如果没找到直接返回-1
- 若找到对应节点node,则将其插入到双向链表表头
- 返回node的value值
插入(SET)操作:
- 根据键值查找hashmap。如果找到,则直接将该节点移到表头即可
- 如果没有找到,首先判断当前Cache是否已满
- 如果已满,则删除表尾节点
- 将新节点插入到表头
AC代码
import java.util.HashMap; public class LRUCache { /** * 声明双向链表节点 * * @author wzy * */ private static class DoubleListNode { DoubleListNode pre; DoubleListNode next; int value; int key; public DoubleListNode(int key, int value) { this.key = key; this.value = value; this.pre = this.next = null; } } private HashMap<Integer, DoubleListNode> hashMap; private DoubleListNode head; private DoubleListNode tail; private int capacity; private int currentSize; public LRUCache(int capacity) { this.capacity = capacity; this.currentSize = 0; hashMap = new HashMap<Integer, LRUCache.DoubleListNode>(capacity); this.head = this.tail = null; } public int get(int key) { DoubleListNode res = hashMap.get(key); if (res != null) { moveNodeToHead(res); return res.value; } else { return -1; } } public void set(int key, int value) { DoubleListNode node = hashMap.get(key); if (node == null) { node = new DoubleListNode(key, value); if (currentSize >= capacity) { hashMap.remove(tail.key); removeNodeFromTail(); } else { currentSize++; } } else { node.value = value; } if (currentSize == 1) { head = node; tail = node; } moveNodeToHead(node); hashMap.put(key, node); } private void moveNodeToHead(DoubleListNode node) { if (node == head) { return; } if (node.next != null) { node.next.pre = node.pre; } if (node.pre != null) { node.pre.next = node.next; } if (node == tail) { tail = node.pre; } if (head != null) { node.next = head; head.pre = node; } head = node; node.pre = null; } private void removeNodeFromTail() { if (tail != null) { if (tail.pre != null) { tail.pre.next = null; } else { head = null; } tail = tail.pre; } } public static void main(String[] args) { LRUCache lruCache = new LRUCache(1); lruCache.set(2, 1); System.out.println(lruCache.get(2)); lruCache.set(3, 2); System.out.println(lruCache.get(2)); System.out.println(lruCache.get(3)); } }
[LeetCode]LRU Cache, 解题报告