LRU机制:哈希表+双向链表 [labuladong-刷题打卡 day9]
今天的知识点LRU缓存机制的实现。学过计组都知道LRU算法(least recently used 最近最少使用算法)是资源管理中的常用算法。那么他是如何实现的呢?
LRU原理和Redis实现

146. LRU 缓存此题算是对LRU机制的一个简化。
为了使查找删除在O(1)中实现,我们结合哈希表和双向链表各自在查找和删除插入中的优势,得到LRU的数据结构:哈希链表,其实在Linux内核中也采用了哈希链表
下面采用带你手撸 LRU 算法
哈希链表的实现
双向链表的实现
双向链表节点
//双向链表节点
class Node {
public:
int key, val;
Node* next;
Node* prev;
Node(int k, int v){
this->key = k;
this->val = v;
}
};
双向链表类
定义节点后,自然就可以构建一个双向链表,并且封装后续需要的插入删除等操作了!
class DoubleList {
private:
// 头尾虚节点
Node* head;
Node* tail;
// 链表元素数
int size;
public:
DoubleList() {
// 初始化双向链表的数据
head = new Node(0, 0);
tail = new Node(0, 0);
head->next = tail;
tail->prev = head;
size = 0;
}
// 在链表尾部添加节点 x,时间 O(1)
void addLast(Node* x) {
x->prev = tail->prev;
x->next = tail;
tail->prev->next = x;
tail->prev = x;
size++;
}
// 删除链表中的 x 节点(x 一定存在)
// 由于是双链表且给的是目标 Node 节点,时间 O(1)
void remove(Node* x) {
x->prev->next = x->next;
x->next->prev = x->prev;
size--;
}
// 删除链表中第一个节点,并返回该节点,时间 O(1)
Node* removeFirst() {
if (head->next == tail)
return nullptr;
Node* first = head->next;
remove(first);
return first;
}
// 返回链表长度,时间 O(1)
int getSize() { return size; }
};
哈希链表类
实现双向链表后,结合哈希表实现get,put方法
get方法主要是根据哈希表查找,如果查找到就移动节点至链表尾,并返回值。
put方法也需要先查找,存在则修改值并移动至链表尾,如果不存在,但已经达到容量,则删除头节点并添加新节点至链表尾
(黑体字为封装的私有函数或需要实现的功能)
class LRUCache {
private:
/* 节点结构体 */
struct Node {
int key, val;
Node* next;
Node(int _key, int _val) : key(_key), val(_val), next(nullptr) {}
};
/* 哈希表存储节点指针 */
unordered_map<int, Node*> map;
/* 虚拟头节点 */
Node* head;
/* 虚拟尾节点 */
Node* tail;
/* 缓存容量 */
int capacity;
public:
LRUCache(int _capacity) : capacity(_capacity) {
/* 初始化虚拟头尾节点 */
head = new Node(-1, -1);
tail = new Node(-1, -1);
head->next = tail;
tail->next = nullptr;
}
int get(int key) {
if (!map.count(key)) {
return -1;
}
/* 通过哈希表定位到节点 */
Node* node = map[key];
/* 将节点移动到链表尾部 */
moveToEnd(node);
return node->val;
}
void put(int key, int value) {
if (map.count(key)) {
/* 如果已存在,修改对应节点的值,并移动到链表尾部 */
Node* node = map[key];
node->val = value;
moveToEnd(node);
return;
}
if (map.size() == capacity) {
/* 如果超出容量,删除链表头部节点 */
deleteHead();
}
/* 添加新的节点到链表尾部,并在哈希表中添加映射 */
addNode(new Node(key, value));
}
private:
/* 将节点移动到链表尾部 */
void moveToEnd(Node* node) {
removeNode(node);
addNode(node);
}
/* 删除链表头部节点 */
void deleteHead() {
Node* node = head->next;
removeNode(node);
map.erase(node->key);
delete node;
}
/* 添加节点到链表尾部 */
void addNode(Node* node) {
tail->next = node;
node->next = nullptr;
map[node->key] = node;
tail = node;
}
/* 删除链表中的节点 */
void removeNode(Node* node) {
Node* prev = head;
while (prev->next != node) {
prev = prev->next;
}
prev->next = node->next;
}
};
本文来自博客园,作者:Alan-Blog,转载请注明原文链接:https://www.cnblogs.com/alanjiang/p/17617883.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步