LRU Cache
Description
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
Solution
Since this is a key, value pair, we can use a HashMap to store the data. The most important thing is how to select a victim page.
If we use a Queue to trace the order of the pages being used, the search time will be O(n) when we want to renew a page.
If we use a priority queue and a timestamp, every time we pop the smallest value of timestamp, time complexity is O(lgN).
How to achieve O(1) time complexity?
Use a Double linked list, with the head contains the recently used Node, the tail contains the least recently used Node. To quickly find a Node in the double linked list, we can use a hash table to map the key to the reference of its Node. By this way, when we use get(key), find the Node and change its position to head, and when cache is full, just remove the node from tail.
class LRUCache {
private Node head, tail;
private int capacity, num;
private HashMap<Integer, Node> hashMap = new HashMap<>();
private static class Node {
private int key, value;
private Node pre, next;
Node(int key, int value) {
this.key = key;
this.value = value;
}
}
public LRUCache(int capacity) {
this.capacity = capacity;
num = 0;
head = new Node(0, 0);
tail = new Node(0, 0);
head.next = tail;
tail.pre = head;
}
// always insert node after head
private void insert(Node node) {
node.pre = head;
node.next = head.next;
head.next.pre = node;
head.next = node;
}
// remove a node from linked list
private void remove(Node node) {
node.pre.next = node.next;
node.next.pre = node.pre;
}
private void moveToFront(Node node) {
remove(node);
insert(node);
}
private void popTail() {
remove(tail.pre);
}
public int get(int key) {
Node node = hashMap.get(key);
if (node == null) return -1;
moveToFront(node);
return node.value;
}
public void put(int key, int value) {
Node node = hashMap.get(key);
if (node == null) {
node = new Node(key, value);
if (num < capacity) {
num++;
hashMap.put(key, node);
insert(node);
}
else {
hashMap.put(key, node);
insert(node);
hashMap.remove(tail.pre.key);
popTail();
}
}
else {
node.value = value;
moveToFront(node);
}
}
}
/**
* 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);
*/
本文详细介绍了如何设计和实现一个最小最近最少使用(LRU)缓存,该缓存支持get和put操作,能够在O(1)的时间复杂度下进行高效的数据存取。通过使用双向链表和哈希表,确保了快速查找和更新缓存中数据的能力。

379

被折叠的 条评论
为什么被折叠?



