比起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;
};