目录
题目描述
请你设计并实现一个满足 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 <= 3000
0 <= key <= 10000
0 <= value <= 105
- 最多调用
2 * 105
次get
和put
思路解析
本题简单来说就是要实现一块大小为capacity的内存,并实现存入键值对,更新值和内存的功能,我们可以创建一个双向链表作为存储的容器,该双向链表应该有四个成员变量,键、值以及前一个节点和后一个节点,再创建一个哈希表用来存储该关键字所对应的节点。
首先肯定是初始化,只需要将头节点和尾节点(头节点用来放入新节点,尾节点用来找到最后一个节点,在超出内存限制的时候需要删除,这也是为什么创建的是双向链表的原因)相连接,将传入的capacity值记录下来即可。
然后是插入节点,分两种情况,如果该链表中没有该关键字,我们只需要新创建一个节点,然后将该节点放到链表首部(头节点的下一个节点)即可,但是如果此时链表中的节点数超出内存,应该删除最后一个节点;如果该链表中已经有该关键字,则在哈希表中找到该节点,更新该节点的val值,再将该节点移动到头节点,这也涉及到移动到头节点的实现,我们可以实现一个函数用来移动到头节点:先将该节点从原先的位置删除,再将该节点放到链表首部。
最后是查找节点,可以直接利用哈希表查找出该节点,然后返回该节点的val值,注意要将该节点移动到链表首部。
注意:在删除节点的时候记得释放存储该节点的指针
代码实现
struct A {//定义一个双向链表
int key,val;//key为关键字,val为数据值
A* l;//l为该节点的前一个节点
A* r;//r为该节点后一个节点
public:
A() : key(0),val(0), l(nullptr), r(nullptr){}
A(int k,int v):l(nullptr),r(nullptr)
{
key=k;
val=v;
}
};
class LRUCache {
private:
unordered_map<int, A*> map;//创建一个无需哈希表用来查询该链表中是否有存在输入的关键字对应的节点
A* head = new A();
A* last = new A();
int flag, cnt=0;//flag用来记录最大容量,cnt用来计当前链表中存节点个数
public:
LRUCache(int capacity)//初始化该双向链表
{
head->r=last;
last->l=head;
flag=capacity;
}
int get(int key) //查询链表中是否存在key
{
if (map.find(key) == map.end()) return -1;//如果不存在返回-1
A*res=map[key];//存在将该节点取出
move_head(res);//将该节点移动到链表首部
return res->val;//返回该节点的val值
}
void put(int key, int value) //向链表中放入元素
{
if (map.find(key) != map.end()) //如果存在
{
A*p=map[key];//取出该key所对应的节点
p->val=value;//更新该节点值
move_head(p);//将该节点移动到链表首部
}
else//如果不存在
{
A*newA=new A(key,value);//创建新节点
map[key]=newA;//向哈希表中添加键值对
addA(newA);//调用函数将新节点插入链表首部
cnt++;//链表中节点数加1
if(cnt>flag)//如果节点数超出flag限制
{
A*thelast=delA();//调用函数,取出并删除最后一个节点
map.erase(thelast->key);//哈希表中也需要删除该关键字
delete thelast;//释放thelast防止产生悬挂指针
cnt--;
}
}
}
void addA(A*newA)//将newA插入链表首部
{
newA->l=head;
newA->r=head->r;
head->r->l=newA;
head->r=newA;
}
void delp(A*p)//删除p节点
{
p->l->r=p->r;
p->r->l=p->l;
}
void move_head(A*p)//将p节点移动到首部
{
delp(p);//先在p节点之前的位置删除p节点
addA(p);//然后再将p节点放入链表首部
}
A* delA()//删除最后一个节点
{
A*thelast=last->l;//将节点取出并返回
delp(thelast);//删除该节点
return thelast;
}
};