LRU缓存结构(最近最少使用)
LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。
最近最少使用算法是大部分操作系统为最大化页面命中率而广泛采用的一种页面置换算法。该算法的思路是,发生缺页中断时,选择未使用时间最长的页面置换出去。
LRU缓存结构采用双端队列和哈希表相结合的方式实现
首先实现一个基本的双向链表节点的结构。
class Node{
public:
int value;
Node* pred;
Node* next;
Node(int value) : value(value), pred(nullptr),next(nullptr){}
};
根据双向链表节点结构Node,实现一种双向链表结构Bidirection。
在该结构中,优先级最低的结点是head头,优先级最高的节点是tail尾。
这个结构有以下几种操作
- 当加入一个节点时,将新加入的节点放在这个链表的尾部,并将这个节点设置为新得尾部,参见addNode方法
- 对这个结构中的任意节点,都可以分离出来并放到整个链表的尾部,参见moveNodeToTail方法
- 移除head节点并返回这个节点,然后将head设置为老head节点的下一个,参见removeHead方法
class Bidirection{
private:
Node* head;
Node* tail;
public:
Bidirection() : head(nullptr),tail(nullptr){}
void addNode(Node* newNode) //加在链表的尾端
{
if(newNode == nullptr)
return;
if(head == nullptr)
{
this->head = newNode;
this->tail = newNode;
}
else
{
this->tail->next = newNode;
newNode->pred = this->tail;
this->tail = newNode;
}
return;
}
void moveNodeToTail(Node* node) //把节点移动到尾端
{
if(node == this->tail)
return;
if(node == this->head)
{
this->head = node->next;
this->head->pred = nullptr;
}
else
{
node->pred->next = node->next;
node->next->pred = node->pred;
}
node->pred = this->tail;
node->next = nullptr;
this->tail->next = node;
this->tail = node;
return;
}
Node* removehead() //去除头部节点
{
if(this->head == nullptr)
return nullptr;
Node* res = this->head;
if(this->head == this->tail)
{
this->head = nullptr;
this->tail = nullptr;
}
else
{
this->head = res->next;
res->next = nullptr;
this->head->pred = nullptr;
}
return res;
}
};
最后实现的LRU缓存结构。就是将记录之间按照“访问经常度”来排序。一旦加入新的记录,就把该记录加到Bidirection的尾部,一旦获得(get)或设置(set)一个记录的key,就将这个key对应的node调整到尾部。一旦cache满了,就删除“最不经常使用”的记录,也就是移除Bidirection的当前头部。
为了能够让每一个key都能找到在Bidirection所对应的节点,同时让每一个node都能找到各自的key,还需要两个map分别记录key到node的映射,以及node到key的映射。
class Solution
{
private:
Bidirection nodeList;
int capacity;
map<int, Node*> mp;
map<Node*, int> mp_;
public:
void get(int k, vector<int>& ans)
{
if(mp.count(k) != 0)
{
Node* res = mp[k];
this->nodeList.moveNodeToTail(res);
ans.push_back(res->value);
return;
}
ans.push_back(-1);
return;
}
void put(int k, int v)
{
if(mp.count(k) != 0)
{
Node* res = mp[k];
res->value = v;
this->nodeList.moveNodeToTail(res);
}
else
{
Node* newnode = new Node(v);
mp.insert(pair<int, Node*>(k, newnode));
mp_.insert(pair<Node* int>(newnode, k));
this->nodeList.addNode(newnode);
if(mp.size() == capacity + 1)
removeMostUnusedCache();
}
return;
}
vector<int> LRU(vector<vector<int>>& operators, int k)
{
if(k < 1)
return vector<int>();
capacity = k;
vector<int> ans;
for(auto vec : operators)
{
if(vec[0] == 1)
put(vec[1], vec[2]);
else if(vec[0] == 2)
get(vec[1], ans);
}
return ans;
}
private:
void removeMostUnusedCache()
{
Node* removeNode = this->nodeList.removehead();
int removeKey = mp_[removeNode];
mp.erase(removeKey);
mp_.erase(removeNode);
return;
}
};