LRU Cache
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get
and put
.
get(key)
- Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.put(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.
The cache is initialized with a positive capacity.
Follow up:
Could you do both operations in O(1) time complexity?
Example:
LRUCache cache = new LRUCache( 2 /* capacity */ ); cache.put(1, 1); cache.put(2, 2); cache.get(1); // returns 1 cache.put(3, 3); // evicts key 2 cache.get(2); // returns -1 (not found) cache.put(4, 4); // evicts key 1 cache.get(1); // returns -1 (not found) cache.get(3); // returns 3 cache.get(4); // returns 4
分析:
因为要把least recently used item在container满了的时候移除掉,所以,我们需要有一个list来mantain这样一个order
同时,因为要取数,所以可以用map来保存数与item的关系。
1 class Node { 2 int key, value; 3 Node pre, next; 4 5 public Node(int key, int value) { 6 this.key = key; 7 this.value = value; 8 } 9 } 10 11 public class LRUCache { 12 HashMap<Integer, Node> map; 13 int capicity; 14 Node head, tail; 15 16 public LRUCache(int capacity) { 17 this.capicity = capacity; 18 map = new HashMap<>(); 19 head = new Node(0, 0); 20 tail = new Node(0, 0); 21 head.next = tail; 22 tail.pre = head; 23 } 24 25 public void deleteNode(Node node) { 26 node.pre.next = node.next; 27 node.next.pre = node.pre; 28 } 29 30 public void addToHead(Node node) { 31 node.next = head.next; 32 node.next.pre = node; 33 node.pre = head; 34 head.next = node; 35 } 36 37 public int get(int key) { 38 if (map.get(key) != null) { 39 Node node = map.get(key); 40 deleteNode(node); 41 addToHead(node); 42 return node.value; 43 } 44 return -1; 45 } 46 47 public void put(int key, int value) { 48 if (map.get(key) != null) { 49 Node node = map.get(key); 50 node.value = value; 51 deleteNode(node); 52 addToHead(node); 53 } else { 54 Node node = new Node(key, value); 55 map.put(key, node); 56 if (map.size() > capicity) { 57 map.remove(tail.pre.key); 58 deleteNode(tail.pre); 59 } 60 addToHead(node); 61 } 62 } 63 }
一种不带dummy head and tail的情况。
1 public class LRUCache { 2 private int cacheSize; 3 private HashMap<Integer, Entry> map;// 缓存容器 4 private Entry first;// 链表头 5 private Entry last;// 链表尾 6 7 public LRUCache(int capacity) { 8 cacheSize = capacity; 9 map = new HashMap<>(); 10 } 11 12 public int get(int key) { 13 Entry node = map.get(key); 14 if (node != null) { 15 moveToHead(node); 16 return node.value; 17 } else { 18 return -1; 19 } 20 } 21 22 private void moveToHead(Entry node) { 23 if (first == null) { 24 first = node; 25 last = node; 26 return; 27 } 28 if (node == first) return; 29 if (node.prev != null) node.prev.next = node.next; 30 if (node.next != null) node.next.prev = node.prev; 31 if (node == last) last = node.prev; 32 33 node.next = first; 34 node.prev = null; 35 first.prev = node; 36 first = node; 37 } 38 39 public void remove(int key) { 40 Entry node = map.get(key); 41 if (node != null) { 42 if (node.prev != null) node.prev.next = node.next; 43 if (node.next != null) node.next.prev = node.prev; 44 if (last == node) last = node.prev; 45 if (first == node) first = node.next; 46 } 47 map.remove(key); 48 } 49 50 public void put(int key, int value) { 51 Entry node = map.get(key); 52 if (node == null) { 53 if (map.size() >= cacheSize) { // remove the last entry
54 map.remove(last.key); 55 if (last.prev != null) { 56 last.prev.next = null; 57 } else { 58 first = null; 59 } 60 last = last.prev; 61 } 62 node = new Entry(key, value); 63 } 64 node.value = value; 65 map.put(key, node); 66 moveToHead(node); 67 } 68 } 69 70 class Entry { 71 Entry prev; 72 Entry next; 73 int value; 74 int key; 75 76 public Entry(int key, int value) { 77 this.key = key; 78 this.value = value; 79 } 80 }