每天一道LeetCode-----实现LFU置换算法
LFU Cache
原題鏈接LFU Cache
實現LFU置換算法,置換規則是當容量滿時換出使用頻率最少的那個,不考慮一定時間內的頻率的情況下,可以采用每個頁使用的次數作為判斷依據,相當于從創建之初到現在的使用頻率
類似的置換算法為了在效率上有一定的保障,通常都是空間換時間,本題也明確規定的時間復雜度是O(1),而O(1)多數情況下都是類似map的存儲結構,當然,C++中map和set是采用紅黑樹實現的,效率是O(lgN),而另一個采用hashtable實現的unordered_map和unordered_set則是O(1)的效率
接下來考慮采用map保存的數據
首先為了根據鍵key找到值value和它的使用頻率freq,可以考慮保存一個類似unordered_map<int, std::pair<int, int>>的結構代表鍵到\<值,頻率>的映射。稱為keyToVF
接下來考慮當容量滿時進行置換的情況,為了讓效率進一步提升,可以借鑒類似操作系統內核處理不同狀態進程控制塊時的策略,即每種狀態的進程控制塊維護一個鏈表,這里就只需為每個頻率維護一個鏈表,再存儲在map中,即unordered_map<int, list<int>>結構代表頻率freq到保存鍵的鏈表的映射。稱為fToList
但是這樣仍然不夠,沒有辦法確定某一時刻最小的頻率是多少,所以還需要使用一個變量記錄當前最小使用頻率minF,當置換時,直接從fToList[minF]中移除一個頁,這里可以事先規定,新加入的都添加到鏈表尾部,移除則移除頭部
當調用get函數時需要對鍵key的頻率進行增加,從keyToVF中可以找到key對應的值和頻率,從fToList中可以找到頻率對應的鏈表,需要做的事情是從這個鏈表中刪除key,然后添加到fToList[freq+1]這個鏈表中。由于鏈表刪除與key相等的節點時需要遍歷鏈表,而刪除指定迭代器處的節點時只需要改變幾個指針,所以為了更進一步提高速度,再次使用一個map保存鍵key到key在鏈表中迭代器的映射即unordered_map<int, list<int>::iterator>。稱為keyToIt
至此三個map都已經構造完成,get和put的工作只是在線性時間操作不同的map而已
代碼如下
class LFUCache { public:LFUCache(int capacity) {capacity_ = capacity;size_ = 0;minF_ = 1;}int get(int key) {/* 不存在key */if(keyToVF_.find(key) == keyToVF_.end()) return -1;/* 將key從key的頻率map中刪除,然后加到freq+1的鏈表中 */fToList_[keyToVF_[key].second].erase(keyToIt_[key]);fToList_[++keyToVF_[key].second].push_back(key);keyToIt_[key] = --fToList_[keyToVF_[key].second].end();/* 更新最小頻率 */if(fToList_[minF_].empty())++minF_;return keyToVF_[key].first;}void put(int key, int value) {if(capacity_ == 0) return;/* 將頻率加一,返回非-1表示存在key,重新設置value即可 */if(get(key) != -1){keyToVF_[key].first = value;return;}if(size_ == capacity_){/* 刪除頻率最小的鏈表頭 */keyToVF_.erase(fToList_[minF_].front());keyToIt_.erase(fToList_[minF_].front());fToList_[minF_].pop_front();--size_;}/* 添加數據到頻率為1的鏈表中 */keyToVF_[key] = std::make_pair(value, 1);fToList_[1].push_back(key);keyToIt_[key] = --fToList_[1].end();minF_ = 1;++size_;} private:unordered_map<int, std::pair<int, int>> keyToVF_;unordered_map<int, list<int>::iterator> keyToIt_;unordered_map<int, list<int>> fToList_;int capacity_;int size_;int minF_; };/*** 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);*/今天才發現csdn的markdown的<>是敏感字符….
總結
以上是生活随笔為你收集整理的每天一道LeetCode-----实现LFU置换算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 每天一道LeetCode-----实现L
- 下一篇: 每天一道LeetCode-----链表插