LRU緩存機制
LRU是頁面置換算法的其中一個,是一種最近最少使用的緩存機制,它支持以下操作
獲取數據 get(key) - 如果密鑰 (key) 存在於緩存中,則獲取密鑰的值(總是正數),否則返回 -1。
寫入數據 put(key, value) - 如果密鑰不存在,則寫入其數據值。當緩存容量達到上限時,它應該在寫入新數據之前刪除最近最少使用的數據值,從而爲新的數據值留出空間。
我們使用O(1)的時間複雜度完成這兩種操作
LRUCache cache = new LRUCache( 2 /* 緩存容量 */ );
cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // 返回 1
cache.put(3, 3); // 該操作會使得密鑰 2 作廢
cache.get(2); // 返回 -1 (未找到)
cache.put(4, 4); // 該操作會使得密鑰 1 作廢
cache.get(1); // 返回 -1 (未找到)
cache.get(3); // 返回 3
cache.get(4); // 返回 4
思路:
使用兩個數據結構,一個是隊列,一個是哈希map
確保通過隊列的結點可以找到哈希map,通過哈希map也可以找到隊列的結點,我們這樣定義
//pair 中的first和second我叫做 key 和value
list< pair<int ,int >> queue;
//m的first是key,這個key和queue中pair的key相對應,m的second的是list的迭代器
unordered_map<int, list<pair<int,int> >::iterator > m;
從上面的結構我們可以發現,通過key經過哈希map的索引我們找到list的迭代器,通過list的迭代器我們就可以找到pair結構的second,也就是value,同樣的,在已知list的迭代器的情況下,我們可以通過迭代器中的key去索引到map的迭代器。
根據上面的敘述我們可以捋一下程序的大致流程:
set操作:
我們用一個隊列去紀錄我們的數據使用的順序,在隊列的最尾端是最早使用的數據,也就是最近使用最少的數據。
1.當set一個key-value後,我們首先尋找這個key是否在哈希map中能找到,如果找到了就找到了對應list中的迭代器了,我們把list對應的那個結點調整到隊列首,表示最新使用的,並且把哈希map中key對應的值重新指向調整後的結點
2.如果沒有找到說明要增加新數據,先判斷容量夠不夠用,如果夠用(size < cap)直接size要++,同時要向隊列中插入一個key-value,並且哈希map中也要生成一個key-value指向list中的結點,如果容量(size == cap)不夠用就要淘汰最不經常使用的結點(實際上就是list中的尾結點),怎麼刪除呢? 先用list中尾結點的key值把哈希map中的映射關係刪除掉,然後再刪除list中的尾結點,最後再添加新結點。
get操作:
get操作就是讀取對應key值對應的value值,需要做的就是每次讀取後,如果key值存在就把key值對應的list結點調整到首部,表示最新使用的,並且調整哈希map中key對應的結點
如果key沒有對應的結點就直接返回-1即可。
代碼
class LRUCache {
public:
LRUCache(int capacity):cap(capacity),size(0) {
if(cap == 0)
assert(false);
}
int get(int key) {
int value = -1;
auto it = key_list_map.find(key);
if(it==key_list_map.end())
{
return -1;
}
else
{
value = it->second->second;
ls.erase(it->second);
ls.push_front(pair<int,int>(key,value));
it->second = ls.begin();
return value;
}
}
void put(int key, int value) {
auto it = key_list_map.find(key);
if(it==key_list_map.end())
{
if(cap == size)
{
//用list中的key刪除unorderedmap中的關係
key_list_map.erase(ls.back().first);
ls.pop_back();
}
else
{
++size;
}
}
else
{
ls.erase(it->second);//用迭代器刪除
}
//重新入一個新的鍵值對
//重新建立映射關係
ls.push_front(pair<int,int>(key,value));
key_list_map[key] = ls.begin();
}
int cap;
int size;
std::list< std::pair<int,int> > ls;
std::unordered_map<int,std::list< std::pair<int,int> >::iterator> key_list_map;
};