请你设计并实现一个满足 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
算法就像搭乐高:带你手撸 LRU 算法 :: labuladong的算法小抄 (gitee.io)
- struct DLinkedNode//双向链表结点类,存储前驱、后继结点位置,存储key、value
- {
- int key;
- int val;
- DLinkedNode* prev;
- DLinkedNode* next;
- DLinkedNode():key(0),val(0),prev(nullptr),next(nullptr){}
- DLinkedNode(int key,int val):key(key),val(val),prev(nullptr),next(nullptr){}
- };
- class LRUCache {
- private:
- unordered_map<int,DLinkedNode*> mapping;//map(key,&DLinkedNode(key,val))
- DLinkedNode* head;
- DLinkedNode* tail;
- int size;
- int capacity;
- public:
- LRUCache(int capacity):capacity(capacity),size(0) {
- head=new DLinkedNode(0,0);
- tail=new DLinkedNode(0,0);
- head->next=tail;
- tail->prev=head;
- }
-
- int get(int key) {
- if(mapping.count(key))//key在哈希表中,因为刚刚使用过,所以提到表头
- {
- DLinkedNode* node=mapping[key];
- moveToHead(node);
- return node->val;
- }
- else {
- return -1;
- }
- }
-
- void put(int key, int value)
- {
- if(mapping.count(key))//key已经在哈希表中,更新val值,提至表头
- {
- DLinkedNode* node=mapping[key];
- node->val=value;
- moveToHead(node);
- }
- else//key不在哈希表中
- {
- DLinkedNode* node=new DLinkedNode(key,value);
- addToHead(node);
- mapping[key]=node;
- size++;
- if(size>capacity)//缓存满了,删除链表尾部结点,删除map中的key
- {
- DLinkedNode* removed= removeTail();
- mapping.erase(removed->key);
- size--;
- }
- }
- }
- void addToHead(DLinkedNode* node)//头插法插入一个结点
- {
- node->next=head->next;
- head->next->prev=node;
- node->prev=head;
- head->next=node;
- }
- void removeNode(DLinkedNode* node)//删除一个结点
- {
- node->prev->next=node->next;
- node->next->prev=node->prev;
- }
- void moveToHead(DLinkedNode* node)//把一个结点提至链表头部
- {
- removeNode(node);
- addToHead(node);
- }
- DLinkedNode* removeTail()//删除链表尾部的一个结点
- {
- DLinkedNode* node=tail->prev;
- removeNode(node);
- return node;
- }
- };