35 LRU缓存

LRU缓存

    • [题解1 双map(差2个testcases)](#题解1 双map(差2个testcases))
    • [题解2 哈希表+双向链表(参考)](#题解2 哈希表+双向链表(参考))
    • [题解3 STL:list+unordered_map](#题解3 STL:list+unordered_map)

请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:
LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。
函数 getput 必须以 O(1) 的平均时间复杂度运行。

提示:

  • 1 <= capacity <= 3000
  • 0 <= key <= 10000
  • 0 <= value <= 105
  • 最多调用 2 ∗ 1 0 5 2 * 10^5 2∗105 次 getput

题解1 双map(差2个testcases)

cpp 复制代码
class LRUCache {
    int LRUcapacity;
    map<int, int> cacheMap;
    map<int, int> usecases;
    int time = 0; 
    static bool cmp(const pair<int, int>& lhs, const pair<int, int>& rhs) {  
        return lhs.second < rhs.second;  
    }  
    
public:
    LRUCache(int capacity) {
        LRUcapacity = capacity;
    }
    
    int get(int key) {
        if(cacheMap.count(key)){
        // 记录访问时刻(value越大代表最近使用)
            usecases[key] = time++;
            return cacheMap[key];
        }
        else return -1;
    }
    
    void put(int key, int value) {
        if(cacheMap.count(key)){
            cacheMap[key] = value;
            usecases[key] = time++;
        }else{
        // 没满足O(1)的时间复杂度
            if(cacheMap.size() + 1 > LRUcapacity){
                // 拿到最早访问的关键字 value最小
                vector<pair<int, int>> usecasesVector(usecases.begin(), usecases.end());
                sort(usecasesVector.begin(), usecasesVector.end(), cmp);
                int idx = usecasesVector[0].first;
                cacheMap.erase(cacheMap.find(idx));
                usecases.erase(usecases.find(idx));
            }
            cacheMap[key] = value;
            usecases[key] = time++;
        }
            
    }
};

/**
 * 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);
 */

题解2 哈希表+双向链表(参考)

cpp 复制代码
class LRUCache {
    int LRUcapacity;
    // 双向链表保证每次找最近使用的操作时间复杂度为O(1)
    struct Node{
        int key;
        int value;
        Node* prev;
        Node* next;
        Node(): key(0), value(0), prev(nullptr), next(nullptr){}
        Node(int key1, int value1): key(key1), value(value1), prev(nullptr), next(nullptr){}
    };
    map<int, Node*> cacheMap;
    Node* head, *tail;
public:
    LRUCache(int capacity) {
        LRUcapacity = capacity;
        head = new Node();
        tail = new Node();
        head->next = tail;
        tail->prev = head;
    }
    
    int get(int key) {
        if(cacheMap.count(key)){
            // 把node添加到头结点H
            // 对于双向链表, 原位置node需要修正的:
            // node->next->prev 和 node->prev->next
            // 目标位置H需要修正的:
            // H->next, H->next->prev
            Node* getNode = cacheMap[key];

            getNode->prev->next = getNode->next;
            getNode->next->prev = getNode->prev;

            getNode->prev = head;
            getNode->next = head->next;

            head->next = head->next->prev = getNode;

            return getNode->value;
        }
        else return -1;
    }
    
    void put(int key, int value) {
        if(cacheMap.count(key)){
            
            Node* getNode = cacheMap[key];
            getNode->value = value;
            
            // 添加到头结点
            getNode->prev->next = getNode->next;
            getNode->next->prev = getNode->prev;

            getNode->prev = head;
            getNode->next = head->next;

            head->next = head->next->prev = getNode;

        }else{
            if(cacheMap.size() + 1 > LRUcapacity){
                Node* pre = tail->prev;
                cacheMap.erase(cacheMap.find(pre->key));
                pre->prev->next = pre->next;
                pre->next->prev = pre->prev;
                // 防止内存泄漏
                delete pre;
                
            }
            cacheMap[key] = new Node(key, value);

            Node* getNode = cacheMap[key];
            // 新结点添加到头结点 (代表最近被使用)
            // 新结点无原位置,所以只需要修改H附近的链
            getNode->prev = head;
            getNode->next = head->next;

            head->next = head->next->prev = getNode;
        }
            
    }
};

题解3 STL:list+unordered_map

cpp 复制代码
class LRUCache {
    const int cap;
    list<pair<int, int>> cache;
    unordered_map<int, decltype(cache.begin())> dict;
public:
    LRUCache(int capacity) : cap(capacity) {}
    
    int get(int key) {
        if (!dict.count(key))
            return -1;
        cache.splice(cache.cend(), cache, dict[key]);
        return dict[key]->second;
    }
    
    void put(int key, int value) {
        if (!dict.count(key)) {
            if (cache.size() == cap) {
                dict.erase(cache.front().first);
                cache.pop_front();
            }

            dict[key] = cache.emplace(cache.cend(), key, value);
        }
        else {
            dict[key]->second = value;
            cache.splice(cache.cend(), cache, dict[key]);
        }
    }
};
相关推荐
To_OC6 小时前
LC 49 字母异位词分组:想到哈希表很简单,选对 key 才是精髓
javascript·算法·leetcode
用户9385156350711 小时前
从 O(n²) 到 O(nlogn):一文读懂快速排序的“快”与“妙”
javascript·算法
To_OC13 小时前
手写快排次次翻车?别死背快排模板了,这才是面试官想听的底层逻辑
javascript·算法·排序算法
饼干哥哥13 小时前
Reddit VOC调研太慢?搭一个AI专家团队半小时洞察任何品类|以猫用饮水机为例
人工智能·算法·ai编程
地平线开发者14 小时前
Transformer模型部署之性能优化指南
算法
地平线开发者15 小时前
人在途中:从“编译失败”到“模型可落地”——CUDA 自定义算子
算法·自动驾驶
半个落月18 小时前
从递归到快速排序:用 JavaScript 把分治思想讲明白
javascript·算法·面试
小月土星19 小时前
JavaScript 快速排序:从 pivot、双指针到分治思想
javascript·算法·面试
小月土星19 小时前
JavaScript 递归入门:从 1 到 n 求和,再到数组扁平化
javascript·算法·面试