https://leetcode.cn/problems/lru-cache/description
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:
LRUCache(int capacity)以 正整数 作为容量capacity初始化 LRU 缓存int get(int key)如果关键字key存在于缓存中,则返回关键字的值,否则返回-1。void put(int key, int value)如果关键字key已经存在,则变更其数据值value;如果不存在,则向缓存中插入该组key-value。如果插入操作导致关键字数量超过capacity,则应该 逐出 最久未使用的关键字。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。
示例:
输入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]
解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1); // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2); // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1); // 返回 -1 (未找到)
lRUCache.get(3); // 返回 3
lRUCache.get(4); // 返回 4
提示:
1 <= capacity <= 30000 <= key <= 100000 <= value <= 105- 最多调用
2 * 105次get和put
思路:利用字典+双向链表结构
C#实现
public class LRUCache {
private class Node {
public int key; // 键
public int value; // 值
public Node prev; // 前驱节点
public Node next; // 后继节点
public Node(int key, int value) {
this.key = key;
this.value = value;
}
}
// 当前缓存大小
private int size;
// 缓存容量上限
private int capacity;
// 哈希表,用于 O(1) 的时间里,直接找到这个 key 对应的节点
private Dictionary<int, Node> map = new Dictionary<int, Node>();
// 双向链表的虚拟头节点和尾节点(哨兵节点)
private Node head;
private Node tail;
public LRUCache(int capacity) {
this.size = 0;
this.capacity = capacity;
// 创建虚拟头尾节点(不存储实际数据)
head = new Node(-1, -1);
tail = new Node(-1, -1);
// 互相连接,形成初始链表结构 head <-> tail
head.next = tail;
tail.prev = head;
}
public int Get(int key) {
if (map.ContainsKey(key)) {
Node node = map[key];
// 先从当前位置删除节点
DeleteNode(node);
// 再移动到头部(最近使用)
AddNodeToHead(node);
return node.value;
}
return -1;
}
public void Put(int key, int value) {
if (!map.ContainsKey(key)) {
// 创建新节点
Node newNode = new Node(key, value);
// 加入哈希表
map[key] = newNode;
// 插入到链表头部
AddNodeToHead(newNode);
size++;
// 若超过容量,移除最久未使用的节点
if (size > capacity) {
RemoveLRUEntry();
}
} else {
// 若节点已存在,则更新 value 并移动到头部
Node node = map[key];
node.value = value;
MoveToHead(node);
}
}
// 将节点插入到链表头部
// 逻辑:head <-> firstNode 变成 head <-> node <-> firstNode
private void AddNodeToHead(Node node) {
node.prev = head;
node.next = head.next;
head.next.prev = node;
head.next = node;
}
// 删除链表中的任意节点
// 逻辑:prev <-> node <-> next 变成 prev <-> next
private void DeleteNode(Node node) {
Node preNode = node.prev;
Node nextNode = node.next;
preNode.next = nextNode;
nextNode.prev = preNode;
}
// 将节点移动到链表头部
// 先删除,再插入到头部
private void MoveToHead(Node node) {
DeleteNode(node);
AddNodeToHead(node);
}
// 弹出(删除并返回)链表尾部节点
// 尾部节点是最久未使用的
private Node PopTail() {
Node node = tail.prev;
DeleteNode(node);
return node;
}
// 移除最久未使用的节点(即尾部节点)
// 同时从哈希表中删除对应 key
private void RemoveLRUEntry() {
Node node = PopTail();
map.Remove(node.key);
size--;
}
}
/**
* 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);
*/


