前兩天有個面試問我了一個問題?拿什麼容器實現LRU最合適
當時腦子很亂,第一次說用堆來存儲,後面又想到改爲鏈表存儲更適合其位置不斷變化的特徵就匆匆過去了
後來自己想了想又查了點資料,最終以下文的方式實現LRU
爲了保持高效的put以及find我們在這裏需要用到底層爲哈希的容器提高查詢效率,加上改變位置的靈活性,這裏我選擇使用了unordered_map以及list的組合方式
class LRUCache {
public:
LRUCache(int capacity) {
_capacity = capacity;
}
int get(int key) {
// 如果key對應的值存在,則listit取出
//這裏就可以看出hashmap的value存的是list的iterator 的好處:找到key
// 就找到key存的值在list中的iterator
//直接刪除,再進行頭插,實現O(1)的數據挪動
auto hashit = _hashmap.find(key);
if(hashit != _hashmap.end()){
auto listit = hashit->second;
pair<int, int> kv = *listit;
_list.erase(listit);
_list.push_front(kv);
_hashmap[key] = _list.begin();
return kv.second;
}
else{
return -1;
}
}
void put(int key, int value) {
// 如果沒有數據則進行插入數據
// 如果有數據則進行數據更新
auto hashit = _hashmap.find(key);
if(hashit == _hashmap.end()){
// 插入數據時,如果數據已經達到上限,則刪除鏈表頭的數據和hashmap中的數據
//兩個刪除操 作都是O(1)
if(_list.size() >= _capacity){
_hashmap.erase(_list.back().first);
_list.pop_back();
}
_list.push_front(make_pair(key,value)); _
hashmap[key] = _list.begin();
}
else{
// 再次put,將數據挪動list前面
auto listit = hashit->second;
pair<int, int> kv = *listit;
kv.second = value;
_list.erase(listit);
_list.push_front(kv);
_hashmap[key] = _list.begin();
}
}
private:
list<pair<int, int>> _list;
// 將近用過的往鏈表的投上移動,保持LRU
size_t _capacity;
// 容量大小,超過容量則換出,保持LRU
unordered_map<int, list<pair<int, int>>::iterator> _hashmap;
//巧妙的設計將unordered_map的value type放成list<pair<int, int>>::iterator
//get一個已有的值可以直接找到key在list中對應的iterator
//然後將這個值移動到鏈表的頭部,保持LRU
};
//測試用例
int main()
{
LRUCache myCache = new LRUCache(2);
myCache.put(1, 1);
myCache.put(2, 2);
cache.get(1);//返回1
cache.put(3, 3);//密鑰2作廢
myCache.get(2);
myCache.put(4, 4);//密鑰1作廢
myCache.get(1);//返回-1,未找到
myCache.get(3);//返回3
myCache.get(4);//返回4
return 0;
}