双向链表版本:
#include <bits/stdc++.h>
using namespace std;
struct Node{
int key, value;
Node* prev;
Node* next;
Node():key(0), value(0), prev(nullptr), next(nullptr){}
Node(int k, int v):key(k), value(v), prev(nullptr), next(nullptr){}
};
class LRUCache{
private:
int capacity_;
int size_;
Node* head;
Node* tail;
unordered_map<int, Node*> cache_;
public:
LRUCache(int capacity): capacity_(capacity), size_(0){
head = new Node();
tail = new Node();
head->next = tail;
tail->prev = head;
}
void removeNode(Node* node){
node->prev->next = node->next;
node->next->prev = node->prev;
}
void addToHead(Node* node){
node->next = head->next;
node->prev = head;
head->next->prev = node;
head->next = node;
}
void moveToHead(Node* node){
removeNode(node);
addToHead(node);
}
Node* removeTail(){
auto node = tail->prev;
removeNode(node);
return node;
}
int get(int key){
if(cache_.find(key) != cache_.end()){
moveToHead(cache_[key]);
return cache_[key]->value;
}
return -1;
}
void put(int key, int value){
if(cache_.find(key) != cache_.end()){
moveToHead(cache_[key]);
cache_[key]->value = value;
}
else{
if(size_ == capacity_){
auto node = removeTail();
cache_.erase(node->key);
delete node;
--size_;
}
auto node = new Node(key, value);
addToHead(node);
cache_.insert({key, node});
++size_;
}
}
~LRUCache(){
while(head){
auto node = head;
head = head->next;
delete node;
}
}
void print(){
cout << "=================" << endl;
auto node = head->next;
while(node != tail){
cout << "[" << node->key << " " << node->value << "]" << endl;
node = node->next;
}
}
};
int main(){
auto lru = new LRUCache(2);
lru->put(1, 1);
lru->put(2, 2);
lru->print();
lru->put(3, 3);
lru->print();
cout << lru->get(2) << endl;
lru->print();
lru->put(4, 4);
lru->print();
cout << lru->get(1) << endl;
lru->print();
cout << lru->get(3) << endl;
return 0;
}
手写双向链表版本比较简单,在纸上画一画就能想通!
STL的list版本:
using pi = pair<int, int>;
class LRUCache {
public:
LRUCache(int capacity):capacity_(capacity)
{}
/**
* 根据给定的键获取缓存中的值。
* 如果键存在于缓存中,则将该键值对移动到缓存的前端,并返回对应的值。
* 如果键不存在于缓存中,则返回-1。
*
* @param key 要查询的键。
* @return 存储在缓存中的键对应的值,如果键不存在则返回-1。
*/
int get(int key) {
// 检查键是否存在于缓存中
if(key_table_.count(key)){
// 获取键对应的节点
auto node = key_table_[key];
// 从节点中提取值
int value = node->second;
// 从缓存中删除节点,因为它即将被移动到前端
cache_.erase(node);
// 将键值对移动到缓存的前端
cache_.push_front({key, value});
// 更新键在映射表中的指针,指向移动到前端的新节点
key_table_[key] = cache_.begin();
// 返回键对应的值
return value;
}
// 如果键不存在,返回-1
return -1;
}
/**
* 将键值对(key, value)放入缓存。
* 如果键已经存在,则更新对应的值,并从缓存中移除该键值对,以便重新插入以维护LRU顺序。
* 如果缓存已满,则移除最不常访问的键值对,为新键值对腾出空间。
*
* @param key 要放入缓存的键。
* @param value 要放入缓存的值。
*/
void put(int key, int value) {
// 如果键已经存在,则获取对应的节点
if(key_table_.count(key)){
auto node = key_table_[key];
int value = node->second;
// 从缓存中移除该节点,因为待会要重新插入以维护LRU顺序
cache_.erase(node);
}
else{
// 如果缓存已满,则移除最不常访问的键值对
if(cache_.size() == capacity_){
auto kv = cache_.back();
int k = kv.first, v = kv.second;
cache_.pop_back();
key_table_.erase(k);
}
}
// 将新的键值对插入到缓存的前端,并更新键映射表
cache_.push_front({key, value});
key_table_[key] = cache_.begin();
}
private:
unordered_map<int, list<pi>::iterator> key_table_;
list<pi> cache_; // 缓存
int capacity_;
};
主要用了迭代器,对于初学者可能难以理解。可以看我的下一篇博客,详细阐述了迭代器设计思想!