146、LRU
为了满足LRU缓存的约束并保证get和put操作的时间复杂度为O(1),我们可以使用哈希表加双向链表的数据结构。哈希表用于快速定位一个节点,双向链表用于维护节点的使用顺序,最近使用的节点被放到链表尾部,最久未使用的节点在链表头部。
146、LRU
以下是一个简单的实现:
import java.util.HashMap;
class LRUCache {
class DLinkedNode {
int key;
int value;
DLinkedNode prev;
DLinkedNode next;
}
private void addNode(DLinkedNode node) {
// Always add the new node right after head.
node.prev = head;
node.next = head.next;
head.next.prev = node;
head.next = node;
}
private void removeNode(DLinkedNode node){
// Remove an existing node from the linked list.
DLinkedNode prev = node.prev;
DLinkedNode next = node.next;
prev.next = next;
next.prev = prev;
}
private void moveToHead(DLinkedNode node){
// Move certain node in between to the head.
removeNode(node);
addNode(node);
}
private DLinkedNode popTail() {
// Pop the current tail.
DLinkedNode res = tail.prev;
removeNode(res);
return res;
}
private HashMap<Integer, DLinkedNode> cache = new HashMap<>();
private int size;
private int capacity;
private DLinkedNode head, tail;
public LRUCache(int capacity) {
this.size = 0;
this.capacity = capacity;
head = new DLinkedNode();
// head.prev = null;
tail = new DLinkedNode();
// tail.next = null;
head.next = tail;
tail.prev = head;
}
public int get(int key) {
DLinkedNode node = cache.get(key);
if (node == null) return -1;
// move the accessed node to the head;
moveToHead(node);
return node.value;
}
public void put(int key, int value) {
DLinkedNode node = cache.get(key);
if(node == null) {
DLinkedNode newNode = new DLinkedNode();
newNode.key = key;
newNode.value = value;
cache.put(key, newNode);
addNode(newNode);
++size;
if(size > capacity) {
// pop the tail
DLinkedNode tail = popTail();
cache.remove(tail.key);
--size;
}
} else {
// update the value.
node.value = value;
moveToHead(node);
}
}
}
这个实现中,DLinkedNode类是双向链表的节点,包含键、值以及指向前一个节点和后一个节点的指针。LRUCache类中包含了一个哈希表cache来存储键和对应节点的映射,以及一个虚拟的双向链表(由head和tail节点作为哨兵节点)来维护所有节点的使用顺序。
addNode方法将新节点添加到双向链表的头部(即head节点之后),表示这个节点是最近使用的。removeNode方法从双向链表中移除一个节点。moveToHead方法将一个节点移动到双向链表的头部,表示这个节点最近被访问过。popTail方法移除双向链表尾部的节点,即最久未使用的节点,并返回这个节点。
get方法首先查找哈希表,如果找到了对应的节点,则将其移动到双向链表的头部,并返回节点的值。如果未找到,则返回-1。
put方法首先查找哈希表,如果未找到对应的节点,则创建一个新节点并添加到双向链表的头部和哈希表中。如果当前缓存的大小超过了容量限制,则移除双向链表尾部的节点,并从哈希表中移除对应的键。如果找到了对应的节点,则更新节点的值,并将其移动到双向链表的头部。

浙公网安备 33010602011771号