力扣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);
 */
相关推荐
AllenO.o15 小时前
Redis五种数据结构详解
java·数据结构·数据库·redis·缓存
yy鹈鹕灌顶1 天前
Redis 基础详解:从入门到精通
数据库·redis·缓存
程序员buddha2 天前
【代码优化篇】强缓存和协商缓存
缓存
我可是ikun啊2 天前
Redis经典面试题
数据库·redis·缓存
天上掉下来个程小白2 天前
缓存套餐-01.Spring Cache入门案例
java·redis·spring·缓存·springboot·springcache
来一杯龙舌兰2 天前
【Bug经验分享】SourceTree用户设置必须被修复/SSH 主机密钥未缓存(踩坑)
运维·缓存·ssh·sourcetree·主机密钥未缓存
chunfeng—2 天前
Redis 主从同步与对象模型(四)
数据库·redis·缓存·集群·哨兵
张哈大2 天前
【 Redis | 实战篇 缓存 】
数据库·redis·笔记·spring·缓存
LLLLLindream2 天前
Redis-商品缓存
数据库·redis·缓存
大G哥2 天前
加速LLM大模型推理,KV缓存技术详解与PyTorch实现
人工智能·pytorch·python·深度学习·缓存