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是Least Recently Used 的缩写,翻译过来就是“最不常使用”,也就是说,LRU缓存把最不常使用的数据移除,让给最新读取的数据。大多数情况下,最常读取的,也是读取次数最多的,所以,利用LRU缓存,我们能够提高系统的performance. LRU cache是非常频繁出现的一道面试题,一般来讲,当我们问到这道题时,面试官往往想得到的答案是利用 doubly linked list + hashtable 实现 LRU Cache, 那么我们现在就来讲一讲如何利用doubly linked list + hashtable实现LRU Cache的。
这里为什么使用Doubly linkd list 而不使用 Linked list的原因是:
当访问一个节点后,我们需要将他从原来的list中删除,然后将它插入到头节点处,删除过程中需要将其前后节点连接起来,单链表做不到。
查询:
- 根据键值查询hashmap,若命中,则返回节点,否则返回null。
- 从双向链表中删除命中的节点,将其重新插入到表头。
- 所有操作的复杂度均为O(1)。
插入:
- 将新的节点关联到Hashmap
- 如果Cache满了,删除双向链表的尾节点,同时删除Hashmap对应的记录
- 将新的节点插入到双向链表中头部
更新:
- 和查询相似
删除:
- 从双向链表和Hashmap中同时删除对应的记录。
1 public class LRUCache { 2 3 private int capacity; 4 private Map<Integer, Entry> nodes; 5 private int currentSize; 6 private Entry first; 7 private Entry last; 8 9 public LRUCache(int capacity) { 10 this.capacity = capacity; 11 currentSize = 0; 12 nodes = new HashMap<Integer, Entry>(); 13 } 14 15 public int get(int key) { 16 Entry node = nodes.get(key); 17 if(node != null){ 18 moveToHead(node); 19 return node.value; 20 } else { 21 return -1; 22 } 23 } 24 25 public void set(int key, int value) { 26 Entry node = nodes.get(key); 27 if(node == null){ 28 if(currentSize >= capacity){ 29 nodes.remove(last.key); 30 removeLast(); 31 } else { 32 currentSize ++; 33 } 34 node = new Entry(); 35 } 36 37 if(currentSize == 1){ 38 first = node; 39 last = node; 40 } 41 42 node.key = key; 43 node.value = value; 44 moveToHead(node); 45 nodes.put(key, node); 46 } 47 48 private void moveToHead(Entry node){ 49 if(node == first) 50 return; 51 52 // delete current node from doubly linked list 53 if(node.pre != null){ 54 node.pre.next = node.next; 55 } 56 if(node.next != null){ 57 node.next.pre = node.pre; 58 } 59 60 if(last == node){ 61 last = node.pre; 62 } 63 64 if(first != null){ 65 node.next = first; 66 first.pre = node; 67 } 68 first = node; 69 node.pre = null; 70 71 } 72 73 private void removeLast(){ 74 if(last != null){ 75 if(last.pre != null){ 76 last.pre.next = null; 77 } else { 78 first = null; 79 } 80 last = last.pre; 81 } 82 } 83 } 84 85 class Entry{ 86 Entry pre; 87 Entry next; 88 int key; 89 int value; 90 }