問題描述:
運用你所掌握的數據結構,設計和實現一個 LRU (最近最少使用) 緩存機制。它應該支持以下操作: 獲取數據 get 和 寫入數據 put 。獲取數據 get(key) - 如果密鑰 (key) 存在於緩存中,則獲取密鑰的值(總是正數),否則返回 -1。寫入數據 put(key, value) - 如果密鑰不存在,則寫入其數據值。當緩存容量達到上限時,它應該在寫入新數據之前刪除最近最少使用的數據值,從而爲新的數據值留出空間。
問題分析:
首先,我們需要詳細解讀一下什麼是LRU算法(Least Recently Used):
其實這是一種緩存淘汰策略。因爲計算機的緩存容量有限,如果緩存滿了就要刪除一些內容,給新的內容騰出空間。但問題是刪除哪些內容呢?這裏給出的規則就是:保存最近使用的數據(表示有用),刪除長時間沒有使用過的數據。
明確了相關定義之後,我們來進行算法設計:
首先需要查找的複雜度爲常數級別的,而且要有順序,知道什麼是最近使用,什麼是最少使用的。
於是綜合上面的這些特點,我們使用hashmap 和鏈表這兩種數據結構相結合的模式。
class LRUCache {
private:
int cap;
//雙鏈表:裝着(key,value)的元組
list<pair<int,int>> cache;
//哈希表:key映射到(key,value)在cache中的位置
unordered_map<int, list<pair<int,int>>::iterator> map;
public:
LRUCache(int capacity) {
this->cap = capacity;
}
int get(int key) {
auto it = map.find(key);
//訪問的key不存在
if(it==map.end()) return -1;
//如果key存在,則把(key,value)放在隊頭
pair<int,int> kv = *map[key];
cache.erase(map[key]);
cache.push_front(kv);
//更新(key,value)在cache中的位置
map[key] = cache.begin();
return kv.second;
}
void put(int key, int value) {
/* 要先判斷 key 是否已經存在 */
auto it = map.find(key);
if (it == map.end()) {
/* key 不存在,判斷 cache 是否已滿 */
if (cache.size() == cap) {
// cache 已滿,刪除尾部的鍵值對騰位置
// cache 和 map 中的數據都要刪除
auto lastPair = cache.back();
int lastKey = lastPair.first;
map.erase(lastKey);
cache.pop_back();
}
// cache 沒滿,可以直接添加
cache.push_front(make_pair(key, value));
map[key] = cache.begin();
} else {
/* key 存在,更改 value 並換到隊頭 */
cache.erase(map[key]);
cache.push_front(make_pair(key, value));
map[key] = cache.begin();
}
}
};
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache* obj = new LRUCache(capacity);
* int param_1 = obj->get(key);
* obj->put(key,value);
*/