LRU算法:
LRU缓存淘汰算法就是一种常用策略。LRU的全称是Least Recently Used,也就是说我们认为最近使用过的数据应该是[有用的],很久都没有用过的数据应该是无用的,内存满了就优先删那些很久没用过的数据。
LRU算法设计:
1.显然cache中的元素必须有时序,以区分最近使用的和久未使用的数据,当容量满了之后要删除最久未使用的那个元素腾出位置。
2.我们要在cache中快速找某个key是否已存在并得到对应的val;
3.每次访问cache中的某个key,需要将这个元素变为最近使用的,也就是说cache要支持在任意位置快速插入和删除元素。
那么,什么数据结构同时符合上述条件呢?哈希表查找快,但数据无固定顺序;链表有顺序之分,插入删除快,但是查找慢。所以结合一下,形成一种新的数据结构:哈希链表。
LRU缓存算法的核心数据结构就是哈希链表,双向链表和哈希表的结合体。
代码:
class Node {
public:
int key, val;
Node* prev, *next;
Node() : key(0), val(0), prev(nullptr), next(nullptr) {}
Node(int k, int v) : key(k), val(v), prev(nullptr), next(nullptr) {}
};
class DoubleList {
public:
DoubleList() {
head = new Node();
tail = new Node();
head->next = tail;
tail->prev = head;
sz = 0;
}
void addNode(Node* node) {
node->next = tail;
node->prev = tail->prev;
tail->prev->next = node;
tail->prev = node;
sz++;
}
Node* removeNode() {
if (head->next == tail) {
return nullptr;
}
Node* rmNode = head->next;
head->next = rmNode->next;
rmNode->next->prev = head;
sz--;
return rmNode;
}
void getNode(Node* node) {
node->prev->next = node->next;
node->next->prev = node->prev;
sz--;
}
int getSize() {return sz;}
private:
Node* head;
Node* tail;
int sz;
};
class LRUCache {
public:
LRUCache(int capacity) {
cap = capacity;
list = new DoubleList();
}
int get(int key) {
if (mp.count(key)) {
Node* cur = mp[key];
list->getNode(cur);
list->addNode(cur);
return cur->val;
}
return -1;
}
void put(int key, int value) {
if (mp.count(key)) {
Node* cur = mp[key];
cur->val = value;
list->getNode(cur);
list->addNode(cur);
} else {
Node* node = new Node(key, value);
mp[key] = node;
list->addNode(node);
if (cap < list->getSize()) {
Node* rmNode = list->removeNode();
mp.erase(rmNode->key);
}
}
}
private:
unordered_map<int, Node*> mp;
DoubleList* list;
int cap;
};
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache* obj = new LRUCache(capacity);
* int param_1 = obj->get(key);
* obj->put(key,value);
*/