[Leetcode] LRU Cache. 哈希表+雙向鏈表之實現

題目

首先來看題目,就是實現一個LRU,最近最少使用。

Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and set.

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
set(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.

思路

get()相當于,set()即是。一次讀或寫意味著對緩存塊使用了一次,該緩存的優先級就提高。

思路比較簡單,基于哈希表雙向鏈表。先通過哈希表查找緩存塊的位置,也就是緩存塊在鏈表中的位置。然后把該緩存塊置于鏈表表頭。如果緩存已經滿了,那就把表尾的緩存塊丟掉,再在表頭插入新的緩存塊。

實現

用C++11實現的時候比較蛋疼,怪自己不夠熟悉STL。
這里用到連個容器:

  • std::list,相當于雙向鏈表。splice(),push_front(),pop_back()的復雜度為$O(1)$。
  • std::unordered_map,相當于哈希表。find()insert(),erase()的復雜度為$O(1)$(單個element)。

這里需要注意的地方有兩點:

  1. 由于這里用了std::list,所以就不能用指針指向list中的element,要用iterator。值得注意的是,std::list的iterator是不會因為插入刪除而改變所指向的element的,而std::vector的iterator是會改變所指向的element的(例如erase了之后)。
    所以在此哈希表可以這樣實現:
    unordered_map<int, list<T>::iterator> cacheMap;
  2. std::list中,element的移動可以用splice()來實現:
    例如:
std::list<int> list = {1,2,3};
auto it = list.begin();
it = std::next(it); //it指向第二個element,2
if (it != list.begin())
{
    // 將list.begin()置于it之后
    list.splice(list.begin(), list, it, std::next(it)); // 2,1,3
}

My Solution

/*
 * LRUCache
 *
 * Using double linked list (stl list) and hash map(stl unordered_map)
 */
 
#include <iostream>
#include <unordered_map>
#include <memory>
#include <list>

using namespace std;

struct KeyVal
{
    int key;
    int value;
};

class LRUCache{
private:
    int capacity;           // cache capacity
    list<KeyVal> cache;     // cache, implement in Double-Linked-List
    unordered_map<int, list<KeyVal>::iterator> cacheMap;    // Hash Map, quickly access to value

public:
    LRUCache(int capacity):
        capacity(capacity) 
    { }
    
    int get(int key) {
        auto it = cacheMap.find(key);
        // Find by key
        if (it != cacheMap.end()) {
            //Find it! Get the value.
            auto kv = it->second;
            // Move to front
            if (kv != cache.begin())
                cache.splice(cache.begin(), cache, kv, next(kv));

            return kv->value;
        }

        return -1;
    }
    
    void set(int key, int value) {
        auto it = cacheMap.find(key);
        // Find by key
        if (it != cacheMap.end() ){
            // find and set new value
            auto kv = it->second;
            kv->value = value;
            // move front
            if (kv != cache.begin())
                cache.splice(cache.begin(), cache, kv, next(kv));
        }
        else {
            // Not found
            if (cacheMap.size() < capacity) {
                KeyVal newKv = {key, value};
                // set in cache
                cache.push_front(newKv);
                // add to map
                cacheMap.insert(make_pair(key, cache.begin()));
            }else {
                // Capacity is not enough
                // Delete the least used value
                int oldKey = cache.back().key;
                cache.pop_back();
                cacheMap.erase(oldKey);
                
                // Add new value
                KeyVal newKv = {key, value};
                cache.push_front(newKv);
                cacheMap.insert(make_pair(key, cache.begin()));
            }
        }
    }
};
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 前言: 詳細介紹: List:元素有放入順序,元素可重復Map:元素按鍵值對存儲,無放入順序Set:元素無放入順序...
    YBshone閱讀 8,731評論 0 17
  • 背景 一年多以前我在知乎上答了有關LeetCode的問題, 分享了一些自己做題目的經驗。 張土汪:刷leetcod...
    土汪閱讀 12,776評論 0 33
  • java筆記第一天 == 和 equals ==比較的比較的是兩個變量的值是否相等,對于引用型變量表示的是兩個變量...
    jmychou閱讀 1,525評論 0 3
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,778評論 18 399
  • 轉至元數據結尾創建: 董瀟偉,最新修改于: 十二月 23, 2016 轉至元數據起始第一章:isa和Class一....
    40c0490e5268閱讀 1,789評論 0 9