题目:
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
一个LRU把我头都搞大了,我好菜。需要写一个doubly linked list,用一个hashmap来存key to node mapping,达到O(1)的删除。
Runtime: 12 ms, faster than 96.31% of Java online submissions for LRU Cache.
Memory Usage: 47.1 MB, less than 98.84% of Java online submissions for LRU Cache.
class LRUCache {
class Node {
Node next;
Node prev;
int key;
int val;
Node() {
}
Node(int key, int val) {
this.key = key;
this.val = val;
}
}
class DoubleLinkedList {
Node dummyHead = new Node();
Node dummyTail = new Node();
DoubleLinkedList() {
dummyHead.next = dummyTail;
dummyTail.prev = dummyHead;
}
public void printList() {
Node curr = dummyHead.next;
while (curr != null) {
System.out.print(curr.key + " ");
curr = curr.next;
}
System.out.println();
}
public void add(Node node) {
Node tail = dummyTail.prev;
tail.next = node;
node.next = dummyTail;
node.prev = tail;
dummyTail.prev = node;
}
public void remove(Node node) {
node.prev.next = node.next;
node.next.prev = node.prev;
node.next = null;
node.prev = null;
}
public void moveToTail(Node node) {
remove(node);
add(node);
}
public Node removeHead() {
Node head = dummyHead.next;
remove(head);
return head;
}
}
private HashMap<Integer, Node> lru = new HashMap<>();
private DoubleLinkedList list = new DoubleLinkedList();
private int capacity;
public LRUCache(int capacity) {
this.capacity = capacity;
}
public int get(int key) {
if (!lru.containsKey(key)) {
return -1;
}
Node node = lru.get(key);
list.moveToTail(node);
// System.out.println("get: " + key);
// list.printList();
return node.val;
}
public void put(int key, int value) {
if (lru.containsKey(key)) {
Node node = lru.get(key);
node.val = value;
list.moveToTail(node);
} else {
if (lru.size() == capacity) {
Node last = list.removeHead();
lru.remove(last.key);
}
Node newNode = new Node(key, value);
lru.put(key, newNode);
list.add(newNode);
}
// System.out.println("put: " + key + ", " + value);
// list.printList();
}
}
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/
下面是曾经的C++笔记,忽略吧。
unordered_map存放key-iterator pair和key-value pair,key-iterator pair用于迅速找到list中的元素,不需要从头遍历。
Runtime: 100 ms, faster than 95.66% of C++ online submissions for LRU Cache.
Memory Usage: 40.7 MB, less than 6.10% of C++ online submissions for LRU Cache.
class LRUCache {
public:
LRUCache(int capacity): capacity(capacity) {
}
int get(int key) {
if (values.count(key) == 0) {
return -1;
}
// update the most recently used
lru.erase(iter[key]); // erase it from the list
lru.push_front(key); // push it to the front of the list
iter[key] = lru.begin(); // update the iter of the key
return values[key];
}
void put(int key, int value) {
// evict the least recently used
if (values.size() == capacity && !values.count(key)) {
iter.erase(lru.back()); // erase the iter of the lru
values.erase(lru.back()); // erase the value of the lru
lru.pop_back(); // pop the lru from the list
}
// update the most recently used
else if (values.count(key)) {
lru.erase(iter[key]); // if it is already in the list, erase it first
}
lru.push_front(key); // push it to the front of the list
iter[key] = lru.begin(); // update the iter of the key
values[key] = value; // update the value of the key
}
private:
int capacity;
list<int> lru;
unordered_map<int, list<int>::iterator> iter;
unordered_map<int, int> values;
};
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache* obj = new LRUCache(capacity);
* int param_1 = obj->get(key);
* obj->put(key,value);
*/
采用list<pair<int, int>>,不需要单独的hashmap来存key-value pair
Runtime: 112 ms, faster than 70.72% of C++ online submissions for LRU Cache.
Memory Usage: 40.2 MB, less than 20.73% of C++ online submissions for LRU Cache.
class LRUCache {
public:
LRUCache(int capacity): capacity(capacity) {
}
int get(int key) {
if (iter.count(key) == 0) {
return -1;
}
// update the most recently used
int value = iter[key]->second;
lru.erase(iter[key]); // erase it from the list
lru.push_front({key, value}); // push it to the front of the list
iter[key] = lru.begin(); // update the iter of the key
return iter[key]->second;
}
void put(int key, int value) {
// evict the least recently used
if (iter.size() == capacity && !iter.count(key)) {
iter.erase(lru.back().first); // erase the iter of the lru
lru.pop_back(); // pop the lru from the list
}
// update the most recently used
else if (iter.count(key)) {
lru.erase(iter[key]); // if it is already in the list, erase it first
}
lru.push_front({key, value}); // push it to the front of the list
iter[key] = lru.begin(); // update the iter of the key
}
private:
int capacity;
list<pair<int, int> > lru;
unordered_map<int, list<pair<int, int>>::iterator> iter;
};
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache* obj = new LRUCache(capacity);
* int param_1 = obj->get(key);
* obj->put(key,value);
*/