O(1)時間編程實現LFU算法

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章