leetcode146- LRU Cache- hard
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.
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
数据结构:DoublyLinkedList+HashMap=LinkedHashMap。Double list是为了可以随便把中间的一个元素放到列表最尾端说明最新;hashMap是为了O(1)找到中间那个元素
Prev, next, key, value
把旧的放左端,新的放右端,满了删旧的,更新放新的。
Follow up: 用单向列表也可以,小trick hashMap里value存前一个节点不存当前节点,这样就又能取到前面的又能取到当前的了node, node.next。
细节:
1.用了几个数据结构,在增删查改时就要保证每个数据结构都被update了!你今天remove的时候只更新了链表忘了更新map了。
2.这题Node的定义里必须要有key,否则会出现你想删最老结点时,你能根据链表head拿到最老结点,但丢失了它的key无法在map里删掉这条记录,那以后get这个key就都还会返回值回去了。
实现:
public class LRUCache { /* * @param capacity: An integer */ private class Node{ // 必须要有这个!不然你删最老节点的时候,不知道老节点key就不能从map移走这条了! public int key; public int val; public Node prev; public Node next; public Node(int key, int val) { this.key = key; this.val = val; this.prev = this.next = null; } } private int capacity; private Map<Integer, Node> mapKeyNode; private Node dummyHead; private Node dummyTail; public LRUCache(int capacity) { // do intialization if necessary this.capacity = capacity; this.mapKeyNode = new HashMap<>(); this.dummyHead = new Node(-1, 100); this.dummyTail = new Node(-1, 100); this.dummyHead.next = this.dummyTail; this.dummyTail.prev = this.dummyHead; } /* * @param key: An integer * @return: An integer */ public int get(int key) { // write your code here if (!mapKeyNode.containsKey(key)) { return -1; } updateKey(key); return mapKeyNode.get(key).val; } /* * @param key: An integer * @param value: An integer * @return: nothing */ public void set(int key, int value) { // write your code here if (mapKeyNode.containsKey(key)) { mapKeyNode.get(key).val = value; updateKey(key); return; } if (mapKeyNode.size() == capacity) { // 切记还要从map里删去!!!两个数据结构那增删查改都是要两边都改的!而且用key去索引不是用val! mapKeyNode.remove(dummyHead.next.key); removeOldestKey(); } Node newNode = new Node(key, value); mapKeyNode.put(key, newNode); updateKey(key); } private void updateKey(int key) { Node node = mapKeyNode.get(key); if (node.prev != null) { node.prev.next = node.next; } if (node.next != null) { node.next.prev = node.prev; } dummyTail.prev.next = node; node.prev = dummyTail.prev; node.next = dummyTail; dummyTail.prev = node; } private void removeOldestKey() { Node node = dummyHead.next; node.prev.next = node.next; node.next.prev = node.prev; // 这是不是个好习惯? node.prev = null; node.next = null; } }