頁面置換算法之LRU緩存機制

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

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