力扣146 - LRU缓存

视频讲解

哈希 + 双向链表

为什么要用双向链表?

快速删除节点(O(1))

如果是单链表的话,删除一个节点时,需要从头遍历,找到前驱节点 ,才能修改 prev->next,导致 O(n) 的时间复杂度

双向链表存储了两个指针,一个指针指向下一个节点,另一个指针指向上一个节点(前驱指针)。所以我们可以根据前驱指针快速找到上一个节点,然后移除掉当前节点。

demo:

cpp 复制代码
class LRUCache {
public:
    struct Node{
        int key,val;
        Node *prev,*next;
        Node(int k,int v) : key(k) , val(v) , prev(nullptr) , next(nullptr){}
    };
    map<int,Node*>mp;
    Node *L,*R; //双哨兵
    int n; //LRU的总数

    //创建操作
    LRUCache(int capacity) {
        n = capacity;
        L = new Node(-1,-1);
        R = new Node(-1,-1);
        L->next = R;
        R->prev = L;
    }

    //获取值操作 (获得值的时候需要注意:如果有值存在哈希表中的话,那么就要将这个值放在最新的地方)
    //比如: L | 2 1 4 | R 
    //我们查询1这个数,那么查完后需要变成: L | 2 4 1 | R 
    int get(int key) {
        if(mp.count(key)){
            Node* node = mp[key];
            remove(node); //在链表中移除该节点 通过双向指针移除
            insert(node->key,node->val); // 在链表中插入该节点
            return node->val;
        }else{
            return -1;
        }
    }
    
    //插入操作
    void put(int key, int value) {
        if(mp.count(key)){
            Node* node = mp[key];
            remove(node);
            insert(key,value); //这里需要用给的key和value而不是node->key和node->val(因为可能插入的是新的值)
        }else{
            if(mp.size() == n){
                Node* node = L->next; //满了,需要移除的节点
                remove(node);
                insert(key,value);
            }else{
                insert(key,value);
            }
        }
    }

    //以下为自定义新增函数 remove是移除节点的函数 insert是插入节点的函数

    //同时在链表和哈希表中删除node
    void remove(Node* node){
        Node* pre = node->prev;
        Node* nxt = node->next;
        pre->next = nxt;
        nxt->prev = pre;
        mp.erase(node->key);
    }

    //同样要同时操作链表和哈希表
    void insert(int key,int val){
        Node* node = new Node(key,val);
        Node* pre = R->prev;//这里是最后一个插入的数

        //向右
        pre->next = node;
        node->next = R;

        //向左
        node->prev = pre;
        R->prev = node;

        mp[key] = node;
    }

};

/**
 * 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);
 */
相关推荐
大猫子的技术日记1 天前
[百题重刷]前缀和 + Hash 表:缓存思想, 消除重复计算
java·缓存·哈希算法
愤怒的山羊1 天前
jetcache List 缓存, json 序列化 泛型解析成了 JsonObject 处理
缓存·json·list
树在风中摇曳1 天前
带哨兵位的双向循环链表详解(含 C 代码)+ LeetCode138 深度解析 + 顺序表 vs 链表缓存机制对比(图解 CPU 层级)
c语言·链表·缓存
斯文~1 天前
「玩透ESA」站点配置阿里云ESA全站加速+自定义规则缓存
阿里云·缓存·云计算·cdn·esa
S***t7141 天前
Python装饰器实现缓存
缓存
天硕国产存储技术站1 天前
3000次零失误验证,天硕工业级SSD筑牢国产SSD安全存储方案
缓存·固态硬盘·国产ssd
前端炒粉1 天前
35.LRU 缓存
开发语言·javascript·数据结构·算法·缓存·js
努力发光的程序员2 天前
互联网大厂Java面试:从Spring Boot到微服务架构
spring boot·缓存·微服务·消息队列·rabbitmq·spring security·安全框架
zero13_小葵司2 天前
JavaScript性能优化系列(八)弱网环境体验优化 - 8.3 数据预加载与缓存:提前缓存关键数据
javascript·缓存·性能优化
CS_浮鱼2 天前
【Linux进阶】mmap实战:文件映射、进程通信与LRU缓存
linux·运维·c++·缓存