Leetcode 146. LRU 缓存 哈希表 + 双向链表

原题链接:Leetcode 146. LRU 缓存


LRU(Least Recently Used,最近最少使用)缓存是一种内存数据管理策略,核心思想是:当缓存空间满时,优先淘汰 "最近一段时间内最少被访问" 的数据,为新数据腾出空间。它的设计目标是利用 "局部性原理"(程序或用户访问数据时,往往会在短时间内重复访问同一批数据),尽可能保留高频访问的数据,从而减少对底层存储(如数据库、硬盘)的依赖,提升数据读取效率。

LRU 的行为围绕 "访问" 和 "淘汰" 两个关键场景展开:

  • 数据访问时(读 / 写):
    • 若数据已在缓存中("命中"),则将其标记为 "最近使用过"(更新它的访问优先级,避免被优先淘汰);
    • 若数据不在缓存中("未命中"),则将其存入缓存;若缓存已满,先淘汰 "最久未访问" 的数据,再存入新数据。
  • 缓存满时淘汰:
    严格选择 "最近一段时间内没有被访问过" 或 "访问频率最低" 的数据进行删除。

LRU 的高效实现需要满足两个核心需求:

  • 快速查找数据(判断是否在缓存中)
  • 快速更新 / 删除数据(调整访问优先级、淘汰旧数据)
  • 经典实现采用 "哈希表 + 双向链表" 的组合

代码参考官解:LRU缓存机制

cpp 复制代码
// 设计一个双向链表结构体
struct Node{
    int key,value;
    Node* prev;
    Node* next;
    Node():key(0),value(0),prev(nullptr),next(nullptr) {};
    Node(int k,int v):key(k),value(v),prev(nullptr),next(nullptr) {};
};

class LRUCache {
private:
    int size;
    int capacity;
    Node* head;
    Node* tail;
    unordered_map<int,Node*> cache;
public:
    LRUCache(int _capacity): capacity(_capacity),size(0) {
        head = new Node();
        tail = new Node();
        head->next = tail;
        tail->prev = head;
    }
    
    int get(int key) {
        // 如果在当前缓存内没有找到
        if(!cache.count(key)){
            return -1;
        }
        // 如果找到
        Node* node = cache[key];
        move_to_head(node);
        return node->value;
    }
    
    void put(int key, int value) {
        // 如果在当前缓存内没有找到
        if(!cache.count(key)){
            // 如果 key 不存在,创建一个新的节点
            Node* node = new Node(key,value);
            // 添加进哈希表
            cache[key]=node;
            size++;
            // 添加至双向链表的头部
            add_to_head(node);
            if(size>capacity){
                // 如果超出容量,删除双向链表的尾部节点
                Node* drop_node = remove_tail();
                // 删除哈希表中对应的项
                cache.erase(drop_node->key);
                // 防止内存泄漏
                delete drop_node;
                size--;
            }
        }
        //如果当前缓存中有
        else{
            // 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
            Node* node = cache[key];
            node->value = value;
            move_to_head(node);
        }
    }
    void add_to_head(Node* node){
        node->prev = head;
        node->next = head->next;
        head->next->prev = node;
        head->next = node;
    }
    void remove_node(Node* node){
        node->prev->next = node->next;
        node->next->prev = node->prev;
    }
    void move_to_head(Node* node){
        remove_node(node);
        add_to_head(node);
    }
    Node* remove_tail(){
        Node* node = tail->prev;
        remove_node(node);
        return 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);
 */
相关推荐
洲覆7 小时前
Redis 核心数据类型:从命令、结构到实战应用
服务器·数据库·redis·缓存
2351611 小时前
【LeetCode】3. 无重复字符的最长子串
java·后端·算法·leetcode·职场和发展
微笑尅乐11 小时前
神奇的位运算——力扣136.只出现一次的数字
java·算法·leetcode·职场和发展
自信的小螺丝钉12 小时前
Leetcode 155. 最小栈 辅助栈
leetcode·
吃着火锅x唱着歌12 小时前
LeetCode 3105.最长的严格递增或递减子数组
算法·leetcode·职场和发展
吃着火锅x唱着歌12 小时前
LeetCode 2765.最长交替子数组
算法·leetcode·职场和发展
墨染点香13 小时前
LeetCode 刷题【91. 解码方法】
算法·leetcode·职场和发展
自信的小螺丝钉13 小时前
Leetcode 4. 两两交换链表中的节点 递归 / 迭代
leetcode·链表
RoboWizard13 小时前
传输无界 金士顿双接口U盘上新抽电脑
运维·人工智能·缓存·电脑·金士顿