关于LeetCode146.LRU Cache 的C++实现
LRU Cache 的功能特点是淘汰最近最少使用的页面,要求访问缓存中的元素和加入缓存表都达到O(1) 的时间复杂度。
我们知道,hashtable可以实现O(1)的查找速度,然而单单使用hashtable无法实现对已缓存的对象实现排序的目的。而排序、O(1)时间插入和删除节点让我们联想到List,如何设计一种数据结构,将这两种数据结构的优点结合在一起呢?
我们可以设计一种hashtable,key指定为缓存对象的key,value指定为链表节点的地址。labuladong的这张图很好的描述了这种数据结构。
值得一提的是,C++没有提供与Java中LinkedHashMap
功能类似的容器,如果我们要强行使用STL,可以参考这篇 https://stackoverflow.com/questions/42072073/equivalent-linkedhashmap-in-c 我个人倾向于自己造一个轮子实现双向链表。这里有个比较坑的点,双向链表的首尾指针最好人为指定为空节点并且不再变动。否则处理的操作会相当相当的麻烦(我调了一个多钟头才调好)
以下是我的代码实现,没有给双向链表设定空头尾节点的版本(虽然最终AC了,但真的好麻烦),不建议使用这个版本!这是坑
struct Node{
int val;
int key;
Node* front = nullptr;
Node* next = nullptr;
Node() {}
Node(int _key, int _val) : key(_key), val(_val) {};
};
class List{
public:
List() : num(0) {}
Node* push_back(int _key, int _val){
Node* n = new Node(_key, _val);
if(head == nullptr){
head = tail = n;
}
else{
tail->next = n;
n->front = tail;
tail = n;
}
++num;
return n;
}
void mv_to_tail(Node* n){
if(n->next == nullptr) return;
if(head == n) head = head->next;
n->next->front = n->front;
if(n->front != nullptr) n->front->next = n->next;
n->front = tail;
n->next = nullptr;
tail->next = n;
tail = n;
}
int del_head(){
int res = head->key;
if(head == tail){
delete head;
head = nullptr;
tail = nullptr;
return res;
}
Node *d = head;
head = head->next;
head->front = nullptr;
delete d;
num--;
return res;
}
int getNum() { return num; }
private:
Node* head = nullptr;
Node* tail = nullptr;
int num;
};
class LRUCache {
public:
LRUCache(int _capacity) : capacity(_capacity) { }
int get(int key) {
if(addrTable.count(key)){
data.mv_to_tail(addrTable[key]);
return addrTable[key]->val;
}
else
return -1;
}
void put(int key, int value) {
if(addrTable.count(key)){
data.mv_to_tail(addrTable[key]);
addrTable[key]->val = value;
}
else if(data.getNum() >= capacity){
addrTable.erase(data.del_head());
addrTable[key] = data.push_back(key, value);
}
else{
addrTable[key] = data.push_back(key, value);
}
}
private:
int capacity;
List data;
unordered_map<int, Node*> addrTable;
};
/**
* 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);
*/
这个版本是使用了空节点的版本,相对容易理解
struct Node{
int val;
int key;
Node* front = nullptr;
Node* next = nullptr;
Node() {}
Node(int _key, int _val) : key(_key), val(_val) {};
};
class List{
public:
List() : num(0) {
head = new Node(-1, -1);
tail = new Node(-1, -1);
head->next = tail;
tail->front = head;
}
Node* push_back(int _key, int _val){
++num;
Node* n = new Node(_key, _val);
n->front = tail->front;
n->next = tail;
tail->front = n;
n->front->next = n;
return n;
}
void mv_to_tail(Node* n){
n->front->next = n->next;
n->next->front = n->front;
n->front = tail->front;
n->next = tail;
tail->front = n;
n->front->next = n;
}
int del_head(){
--num;
Node* d = head->next;
d->front->next = d->next;
d->next->front = d->front;
int res = d->key;
delete d;
return res;
}
int getNum() { return num; }
private:
Node* head = nullptr;
Node* tail = nullptr;
int num;
};
class LRUCache {
public:
LRUCache(int _capacity) : capacity(_capacity) { }
int get(int key) {
if(addrTable.count(key)){
data.mv_to_tail(addrTable[key]);
return addrTable[key]->val;
}
else
return -1;
}
void put(int key, int value) {
if(addrTable.count(key)){
data.mv_to_tail(addrTable[key]);
addrTable[key]->val = value;
}
else if(data.getNum() >= capacity){
addrTable.erase(data.del_head());
addrTable[key] = data.push_back(key, value);
}
else{
addrTable[key] = data.push_back(key, value);
}
}
private:
int capacity;
List data;
unordered_map<int, Node*> addrTable;
};
/**
* 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);
*/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· 因为Apifox不支持离线,我果断选择了Apipost!