LRU(Least Recently Used,最近最少使用)是一种常见的缓存淘汰算法,用于在有限的缓存空间中管理最近访问的数据。LRU 算法的基本思想是,当缓存空间已满时,将最久未被访问的数据项淘汰出去,为新的数据项让出空间。
LRU 算法的核心原则是基于时间局部性(Temporal Locality):最近被访问的数据很有可能在近期内再次被访问,因此应该优先保留这些数据。算法的实现通常使用一个数据结构(例如链表)来维护数据项的访问顺序,同时使用一个哈希表(或字典)来实现数据项的快速查找。
以下是 LRU 算法的详细步骤:
初始化缓存空间和数据结构
确定缓存的最大容量,创建一个数据结构用于存储缓存数据项的顺序。通常选择双向链表,其中链表头部表示最近访问的数据项,链表尾部表示最久未被访问的数据项。
数据访问操作(get):
当访问某个数据项时,首先检查该数据项是否存在于缓存中(通过哈希表进行查找)。
如果数据项存在于缓存中,将该数据项从当前位置移动到链表头部,表示它是最近被访问的数据项。
如果数据项不存在于缓存中,返回相应的缺失值(如 -1)。
数据插入操作(put):
当插入一个新的数据项时,首先检查该数据项是否存在于缓存中。如果数据项存在于缓存中,更新该数据项的值,并将其移动到链表头部,表示它是最近被访问的数据项。如果数据项不存在于缓存中,进行以下判断:
如果缓存已满(达到最大容量),则需要淘汰链表尾部的数据项,即最久未被访问的数据项。在缓存中插入新的数据项,并将其移动到链表头部。
缓存淘汰:
当缓存已满时,需要淘汰链表尾部的数据项,即最久未被访问的数据项。
从链表尾部删除该数据项,并从哈希表中删除对应的键值对。
LRU 算法通过维护数据项的访问顺序,将最近访问的数据项放在链表的前面,保证了频繁访问的数据项能够快速被命中,提高缓存的命中率。同时,淘汰最久未被访问的数据项,保证了缓存空间的有效利用。
需要注意的是,LRU 算法的实现可以根据具体需求进行优化。例如,可以使用哈希表和双向链表结合的数据结构来提高访问和插入操作的效率,或者利用现有的数据结构库来简化实现过程。
C++简单实现
#include<iostream>
#include<unordered_map>
struct Node
{
int key;
int value;
Node* prev;
Node* next;
Node(int key, int value):key(key),value(value),prev(nullptr),next(nullptr){}
};
class LRUCache
{
public:
LRUCache(int capacity):capacity(capacity)
{
head = new Node(0,0);
tail = new Node(0,0);
head->prev = nullptr;
head->next = tail;
tail->prev = head;
tail->next = nullptr;
}
~LRUCache()
{
for (auto& iter:cache)
{
delete iter.second;
}
cache.clear();
};
public:
int capacity;
Node* head;
Node* tail;
std::unordered_map<int, Node*> cache;
public:
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()) {
cache[key]->value = value;
moveToHead(cache[key]);
} else {
if (cache.size() == capacity) {
Node* lastNode = tail->prev;
removeNode(lastNode);
cache.erase(lastNode->key);
delete lastNode;
}
Node* newNode = new Node(key, value);
cache[key] = newNode;
addToHead(newNode);
}
}
void moveToHead(Node* node)
{
removeNode(node);
addToHead(node);
}
void removeNode(Node* node)
{
node->prev->next = node->next;
node->next->prev = node->prev;
node->prev = nullptr;
node->next = nullptr;
}
void addToHead(Node* node)
{
node->next = head->next;
node->prev = head->prev;
head->next->prev = node;
head->next = node;
}
};
int main()
{
LRUCache cache(2); // 创建一个容量为 2 的缓存
cache.put(1, 1);
cache.put(2, 2);
std::cout << cache.get(1) << std::endl; // 输出 1
cache.put(3, 3); // 缓存已满,淘汰键 2
std::cout << cache.get(2) << std::endl; // 输出 -1,因为键 2 被淘汰
cache.put(4, 4); // 缓存已满,淘汰键 1
std::cout << cache.get(1) << std::endl; // 输出 -1,因为键 1 被淘汰
std::cout << cache.get(3) << std::endl; // 输出 3
std::cout << cache.get(4) << std::endl; // 输出 4
}