LRU实现
实现原理
- 数据结构unordered_map+Dqueue;
- unordered_map的键为key,值为Node*,方便快速存取数据;
- Dqueue双端队列,快速删除、插入Node*;
讨论
简单的LRU是存在缺点的,比如有某一次缓存操作是完全随机的,但是LRU缓存中替换为了这一次随机的访问数据(指的是,只有这一次被访问,接下来不会被访问),导致缓存被污染;
因此也有对LRU的改进版,比如LRU-k,新增加一个历史队列,只有在历史队列中的缓存访问次数达到k次,才会被放置到实际的缓存队列中;至于如何管理历史队列,可以使用FIFO,也可以使用LRU。(当k=1时,即退化为普通的LRU算法)
源码
#include<unordered_map>
using namespace std;
class LRUCache {
private:
//节点元素
struct Node {
int key, val;
Node* prev;
Node* next;
Node() :prev(NULL), next(NULL) {}
};
//循环队列
class Dqueue {
private:
Node* head;
public:
Dqueue() {
head = new Node();
head->prev = head;
head->next = head;
}
~Dqueue() {
while (head->next != head) {
Node* node = pop_front();
delete node;
}
delete head;
}
bool isEmpty() {
return head->next == head;
}
void erase(Node* node) {
node->prev->next = node->next;
node->next->prev = node->prev;
node->next = node->prev = NULL;
}
void push_back(Node* node) {
head->prev->next = node;
node->prev = head->prev;
head->prev = node;
node->next = head;
}
Node* pop_front() {
Node* front = head->next;
erase(front);
return front;
}
};
Dqueue queue;
unordered_map<int, Node*>map;
int size = 0;
int capacity;
public:
LRUCache(int capacity) :capacity(capacity) {
}
int get(int key) {
if (map.find(key) == map.end())
return -1;
Node* node = map[key];
queue.erase(node);
queue.push_back(node);
return node->val;
}
void put(int key, int value) {
if (capacity < 1) return;
Node* node = NULL;
if (map.find(key) != map.end()) {
node = map[key];
node->val = value;
queue.erase(node);
}
else {
if (size < capacity) {
node = new Node();
++size;
}
else {
node = queue.pop_front();
map.erase(node->key);
}
node->key = key;
node->val = value;
map[key] = node;
}
queue.push_back(node);
}
};
LFU实现
实现原理
- 数据结构unordered_map+Dqueue;
- keyMap的键为key,值为Node*,方便快速存取数据;FrepMap的键为freq频率,值为DQueue,可以支持快速查找最少访问频率的数据,并进行基于频率的数据管理;
- Dqueue双端队列,快速删除、插入Node*;
源码
Dqueue版本
class LFUCache {
private:
//节点元素
struct Node {
int key, val, freq;
Node* prev;
Node* next;
Node() :prev(NULL), next(NULL) {}
};
//循环队列
class Dqueue {
private:
Node* head;
public:
Dqueue() {
head = new Node();
head->prev = head;
head->next = head;
}
~Dqueue() {
while (head->next != head) {
Node* node = pop_front();
delete node;
}
delete head;
}
bool isEmpty() {
return head->next == head;
}
void erase(Node* node) {
node->prev->next = node->next;
node->next->prev = node->prev;
node->next = node->prev = NULL;
}
void push_back(Node* node) {
head->prev->next = node;
node->prev = head->prev;
head->prev = node;
node->next = head;
}
Node* pop_front() {
Node* front = head->next;
erase(front);
return front;
}
};
unordered_map<int, Node*>keyMap;
unordered_map<int, Dqueue>freqMap;
int size = 0;
int capacity;
int minFreq = 1;
//更新key的频率
void updateFreq(int key) {
Node* node = keyMap[key];
freqMap[node->freq].erase(node);
if (freqMap[node->freq].isEmpty()) {
freqMap.erase(node->freq);
if (node->freq == minFreq)
++minFreq;
}
++node->freq;
freqMap[node->freq].push_back(node);
}
public:
LFUCache(int capacity):capacity(capacity) {
}
int get(int key) {
if (keyMap.find(key) == keyMap.end())
return -1;
Node* node = keyMap[key];
updateFreq(key);
return node->val;
}
void put(int key, int value) {
if (capacity < 1) return;
if (keyMap.find(key) != keyMap.end()) {
Node* node = keyMap[key];
node->val = value;
updateFreq(key);
return;
}
Node* node = NULL;
if (size >= capacity) {
node = freqMap[minFreq].pop_front();
keyMap.erase(node->key);
if(freqMap[minFreq].isEmpty())
freqMap.erase(minFreq);
}
else {
node = new Node();
++ size;
}
minFreq = 1;
node->key = key;
node->val = value;
node->freq = minFreq;
keyMap[key] = node;
freqMap[node->freq].push_back(node);
}
};
list版本
class LFUCache {
private:
struct Node {
int key, val;
int freq;
Node(int k, int v, int f) :key(k), val(v), freq(f) {}
};
unordered_map<int, list<Node>::iterator>keyMap;
unordered_map<int, list<Node>>freqMap;
int minFreq = 1;
int size = 0;
int capacity;
void updateFreq(int key) {
if (keyMap.find(key) == keyMap.end())
return;
auto& iter = keyMap[key];
Node node(iter->key, iter->val, iter->freq);
freqMap[node.freq].erase(iter);
if (freqMap[node.freq].empty()) {
freqMap.erase(node.freq);
if (node.freq == minFreq)
++minFreq;
}
++node.freq;
freqMap[node.freq].push_front(node);
keyMap[key] = freqMap[node.freq].begin();
}
public:
LFUCache(int capacity) :capacity(capacity) {
}
int get(int key) {
if (keyMap.find(key) == keyMap.end())
return -1;
updateFreq(key);
return keyMap[key]->val;
}
void put(int key, int value) {
if (capacity < 1) return;
if (keyMap.find(key) != keyMap.end()) {
auto& iter = keyMap[key];
iter->val = value;
updateFreq(key);
return;
}
else if (size >= capacity) {
auto& node = freqMap[minFreq].back();
keyMap.erase(node.key);
freqMap[minFreq].pop_back();
if (freqMap[minFreq].empty())
freqMap.erase(minFreq);
--size;
}
Node node(key, value, 1);
minFreq = 1;
freqMap[minFreq].push_front(node);
keyMap[key] = freqMap[minFreq].begin();
++size;
}
};