1.LRU算法介绍
缓存满了需要删除一些缓存,LRU(Least Recently Used)就是一种常用的策略,表示缓存数据的重要程度与最后一次使用时间有关,缓存满了,就先删除很久没有用过的缓存数据。
2.算法要求
leetcode例题https://leetcode-cn.com/problems/lru-cache/初始化构造函数需要接受一个capacity参数作为缓存最大容量。
put(key,val)方法存入键值对。
get(key,val)方法获取key对应的val,不存在则返回-1。
要求put和get的时间复杂度都为O(1),则LRUCache这个数据结构必备条件如下:
- LRUCache中的元素必须有时间顺序,容量满了可以直接删除未使用的元素。
- LRUCache能快速查找key对应的val。
- LRUCache能够在任意位置快速插入和删除元素。
3.数据结构设计
哈希表查询为O(1),但是键值对之间没有顺序,链表有顺序,插入删除快,但是查找慢。
LRU算法的核心就是哈希链表,是双向链表和哈希表的结合体。
- 每次默认从链表尾部添加元素,越靠近头部的元素就是越久没用的缓存。
- 对于一个key,可以通过哈希表快速定位到链表中的节点。
- 链表支持快速删除和插入,这里借助哈希表能快速查找到链表的节点。
4.代码实现
4.1 双向链表节点
struct Node{
int key,val;
Node* next;
Node* prev;
Node(int k,int v){
this->key=k;
this->val=v;
this->next=nullptr;
this->prev=nullptr;
}
};
4.2 双向链表以及其操作
class DoubleList{
private:
Node* DummyHead;
Node* DummyTail;
int _size;
public:
DoubleList(){
//初始化双向链表数据
DummyHead=new Node(0,0);
DummyTail=new Node(0,0);
DummyHead->next=DummyTail;
DummyTail->prev=DummyHead;
_size=0;
}
void addLast(Node* x){
//在链表最后添加节点,O(1)
x->prev=DummyTail->prev;
x->next=DummyTail;
DummyTail->prev->next=x;
DummyTail->prev=x;
_size++;
}
void remove(Node* x){
//删除列表中的节点
x->prev->next=x->next;
x->next->prev=x->prev;
_size--;
}
Node* removeFirst(){
//删除第一个节点,并返回。
if(DummyHead->next == DummyTail){
return nullptr;
}
Node* first=DummyHead->next;
remove(first);
return first;
}
int size(){
return _size;
}
};
4.3 LRUCache
其中省略哈希表的具体实现,借用STL中的unordered_map
class LRUCache {
private:
unordered_map <int,Node*>map;
DoubleList *cache;
int capacity;
/*
*将某个key提升为最近使用的元素
*/
void makeRecently(int key){
Node* x=map.at(key);
cache->remove(x);
cache->addLast(x);
}
/*
*添加最近使用的元素
*/
void addRecently(int key,int val){
Node* x=new Node(key,val);
cache->addLast(x);
map[key]=x;
}
/*
*删除某个key
*/
void deleteKey(int key){
Node * x=map.at(key);
cache->remove(x);
map.erase(key);
delete x;
}
/*
*删除最久未使用的元素
*/
void removeLeastRecently(){
Node* deleteNode=cache->removeFirst();
int deleteKey=deleteNode->key;
map.erase(deleteKey);
delete deleteNode;
}
public:
LRUCache(int capacity) {
this->capacity=capacity;
cache=new DoubleList();
}
int get(int key) {
if(map.find(key)==map.end()){
return -1;
}
makeRecently(key);
return map.at(key)->val;
}
void put(int key, int value) {
if(map.find(key)!=map.end()){
deleteKey(key);
addRecently(key,value);
return;
}
if(capacity==cache->size()){
removeLeastRecently();
}
addRecently(key,value);
}
};