比起LRU算法,LFU算法就是多了一个优先级,LRU是最近最少使用淘汰,LFU是在使用次数最少的前提下淘汰最早的那个页面。为此跟上次一样,用一个 unordered_map mkey,跟一个双向链表存储节点,不同的是我们需要加一个unordered_map mcount, mount【i】存储的是所有访问次数为i的最后一个节点。接下来就简单了,只要某个节点被访问,就令它的次数num加1,从原来的位置摘除,并插入到次数为num的地方。如果这里已经有次数为num 的节点,则插入到mcount【num】这地址的后面,如果不存在,就插入到原来的mcount【num-1】的后面,成为第一个次数为num的节点,具体看代码跟注释。
class LFUCache {
public:
struct node {
int key, val, count;;
node* next;
node* front;
node() :key(0), val(0), count(0),next(NULL),front(NULL) {};
};
LFUCache(int capacity) {
maxsize = capacity;
head = new node();
tail = new node();
head->next = tail;
tail->front = head;
mcount[0] = head;
}
//获取页面
int get(int key) {
if (!mkey.count(key)) return -1;
visied(mkey[key]);
printf();
return mkey[key].val;
}
//插入页面
void put(int key, int value) {
if (mkey.size() == maxsize)
pop();
mkey[key].key = key;
mkey[key].val = value;
visied(mkey[key]);
printf();
}
//节点被访问后,应该怎样维护链表
void visied(node& n) {
if (n.front)
n.front->next = n.next;
if (n.next)
n.next->front = n.front;
//如果节点已经在链表里,将它的前后两端连接,把节点暂时摘出来
++n.count; //访问次数加1
if (&n == mcount[n.count - 1])
mcount[n.count - 1] = n.front;//如果要被摘除的n正好是该数量的最后一个节点,则把
//mcount[n.count - 1]记录的地址前移
node* temp = NULL;
if (mcount[n.count] != NULL)
temp = mcount[n.count];
else
temp = mcount[n.count - 1];
//如果数量为n.count的链表地址存在,则temp直接等于该地址指向的节点,如果不存在
//则等于n.count-1的地址,这个地址一定存在。
temp->next->front = &n;
n.next = temp->next;
temp->next = &n;
n.front = temp;//把n插进去
mcount[n.count] = &n;更新mcount[n.count]的地址
if (mcount[n.count - 1]->count != n.count - 1) mcount[n.count - 1] = NULL;
//如果mcount[n.count - 1]指向了它前面的节点,则令它等于0
}
void pop() {
node* temp = head->next;
head->next = temp->next;
temp->next->front = head; //准备删除头结点指向的节点,并使得头结点指向下一个节点
if (temp->count != head->next->count)//证明要删除的节点是节点所在数量的最后一个
mcount[temp->count] = NULL;
mkey.erase(temp->key);
}//弹出页面
void printf() {
node* p = head;
while (p) {
cout << p->val << " ";
p = p->next;
}
cout << endl;
p = tail;
while (p) {
cout << p->val << " ";
p = p->front;
}
cout << endl << endl;
}//打印存储的所有页面
private:
unordered_map<int, node*> mcount;
unordered_map<int, node> mkey;
node* head; //总是指向使用次数最少,且最早的那个节点
node* tail;
int maxsize;
};