LFU算法:
LFU(Least Frequently Used)算法每次淘汰使用次数最少的数据。
LFU相当于是把数据按照访问频次进行排序,这个需求恐怕没有那么简单,而且还有一种情况,如果多个数据拥有相同的访问频次,我们就得删除最早插入的那个数据。也就是说LFU算法是淘汰访问频次最低的数据,如果访问频次最低的数据有多条,需要淘汰最久的数据。
LFU算法设计:
1.首先,肯定需要freq到key的映射,用来找到freq最小的key。
2.将freq最小的key删除,那你就得快速得到当前所有key最小的freq是多少。想要时间复杂度O(1)的话,肯定不能遍历一遍去找,那就用一个变量minFreq来记录当前最小的freq
3.可能有多个key拥有相同的freq,所以freq对key是一对多的关系,即一个freq对应一个key的列表
4.希望freq对应的key的列表是存在时序的,便于快速查找并删除最旧的key
5.希望能够快速删除key列表中任何一个key,因为如果频次freq的某个key被访问,那么它的频次就会变成freq+1,就应该从freq对应的key列表中删除,接到freq+1对应的key的列表中。
理解:
主要维护三个表KV(key=>value的映射表)、KF(key=>freq的映射表)、FK(freq=>listhash的映射表)。
listhash是一个哈希链表,自己实现,用于记录某些key使用频次相同时key的时序分布表。
此外,通过minfreq记录所有key值使用的最小频次。
代码:
class Node {
public:
Node() : key(0), val(0), prev(nullptr), next(nullptr) {};
Node(int key, int val) : key(key), val(val), prev(nullptr), next(nullptr) {};
int key, val;
Node* prev;
Node* next;
};
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++;
mp[node->key] = node;
}
void seqNode(Node* node) {
node->next->prev = node->prev;
node->prev->next = node->next;
sz--;
mp.erase(node->key);
}
Node* removeNode() {
if (head->next == tail) {
return nullptr;
}
Node* rm = head->next;
head->next = rm->next;
rm->next->prev = head;
sz--;
mp.erase(rm->key);
return rm;
}
unordered_map<int, Node*> mp;
private:
int sz;
Node* head, * tail;
};
class LFUCache {
public:
LFUCache(int capacity) {
cap = capacity;
minFreq = 0;
}
int get(int key) {
}
void put(int key, int value) {
if (KV.count(key)) {
KV[key] = value;
increaseFreq(key);
} else {
KV[key] = value;
cap++;
}
}
void increaseFreq(int key) {
// key对应的freq加一
int key_freq = KF[key];
KF[key]++;
DoubleList* cur = FK[key_freq];
DoubleList* next;
if (FK.count(key_freq + 1)) {
next = FK[key_freq + 1];
} else {
next = new DoubleList();
}
Node* node = cur->mp[key];
cur->seqNode(node);
next->addNode(node);
FK[key_freq] = next;
}
private:
//key->val映射,简称KV表
unordered_map<int, int> KV;
//key->freq映射,简称KF表
unordered_map<int, int> KF;
//freq->keys映射,简称FK表
unordered_map<int, DoubleList*> FK;
//记录最小的频次
int minFreq;
//记录LFU缓存的最大容量
int cap;
};
/**
* Your LFUCache object will be instantiated and called as such:
* LFUCache* obj = new LFUCache(capacity);
* int param_1 = obj->get(key);
* obj->put(key,value);
*/
学习参考:labuladong 的知识店铺