如何实现LRU(Least Recent Used)
为什么需要LRU?操作系统课程中,内存不够的场景下,淘汰旧内容的策略。
基于HashMap和双向链表实现LRU
使用HashMap存储key,这样可以做到put和get的时间都是O(1)。而HashMap的Value指向双向链表实现的LRU的Node节点。
1.put(key,value):首先在HashMap找到key对应的节点,如果节点存在,更新节点的值,并把这个节点移到表头。如果不存在,需要构造新的节点,并且尝试把节点塞到队头。如果LRU空间不足,则通过tail淘汰掉队尾的节点,同时在HashMap中移除Key。
2.get(key):通过查找HashMap找到LRU链表节点,因为根据LRU原理,这个节点是最新访问的,所以要把节点插入到队头,然后返回缓存的值。
文章代码链接
https://github.com/cpselvis/leetcode/blob/master/solution146.cpp
删除和插入双链表的知识:
双向链表的插入与删除(c++实现)
双向链表的插入及删除图解
https://github.com/cpselvis/leetcode/blob/master/solution146.cpp
c++代码
#include <iostream>
#include <map>
using namespace std;
struct CacheNode {
int key;
int value;
CacheNode *pre, *next;
CacheNode(int k,int v) : key(k),value(v), pre(NULL), next(NULL){}
};
class LRUCache {
private:
int size;
CacheNode *head, *tail;//使用伪头部和伪尾部节点
map<int, CacheNode *> mp;
public:
LRUCache(int capacity){
size = capacity;
head = NULL;
tail = NULL;
}
int get(int key) {
map<int, CacheNode *>::iterator it = mp.find(key);
if (it != mp.end()) {
//如果key存在,先通过哈希表定位,再移到头部
CacheNode *node = it->second;
remove(node);
setHead(node);
return node->value;
}
else {
return -1;
}
}
void set(int key, int value) {
map<int, CacheNode *>::iterator it = mp.find(key);
if (it != mp.end()) { //如果key存在,先通过哈希表定位,再修改value并移到头部
CacheNode *node = it->second;
node->value = value;
remove(node);
setHead(node);
}
else {//如果key不存在,创建一个新节点
CacheNode *newNode = new CacheNode(key, value);
if (mp.size() >= size) {
map<int, CacheNode *>::iterator iter = mp.find(tail->key);
remove(tail);
mp.erase(iter);
}
setHead(newNode);
mp[key] = newNode;
}
}
//双链表的删除
/*
第一步:找到即将被删除的节点 p
第二步:将 p 的前驱的后继指向 p 的后继,即 p->prior->next = p->next;
第三步:将 p 的后继的前驱指向 p 的前驱,即 p->next->prior = p->prior;
第四步:删除节点 p 即 delete p;
*/
void remove(CacheNode *node) {
if (node->pre != NULL) {
node->pre->next = node->next;
}
else {
head = node->next;
}
if (node->next != NULL) {
node->next->pre = node->pre;
}
else {
tail = node->pre;
}
}
/*
当在链表头部插入节点,只需把新节点的右指针指向链表原来的第一个节点,再把原链表头左指针指向新节点即可,
用代码描述:
insertnode->right= head;
head->left = insertnode;
head = insertnode;
*/
void setHead(CacheNode *node) {
node->next = head;
node->pre = NULL;
if (head != NULL) {
head->pre = node;
}
head = node;
if (tail == NULL) {
tail = head;
}
}
};
int main(int argc, char **argv) {
LRUCache *lruCache = new LRUCache(2);
lruCache->set(2, 2);
lruCache->set(1, 1);
cout << lruCache->get(2) << endl;
lruCache->set(4, 4);
cout << lruCache->get(1) << endl;
cout << lruCache->get(4) << endl;
}