CodeTop之LRU缓存

题目链接

146. LRU 缓存 - 力扣(LeetCode)

题目解析

算法原理

我们使用双向链表+哈希表的形式来模拟缓存机制

首先我们要自己实现一个双链表, 自己写一个内部类, 这个内部类记录了key,value,prev,next(前驱和后继), 后续我们就通过这个内部类来构造双向链表

其次我们要把LRU缓存机制和我们的双向链表联系起来

我们每次查找一个Key所对应的value, 如果存在的话, 那么就相当于这个key-value组合是常常访问的, 因此我们要把它的优先级提高, 具体的代码就是我们把这个key-value的结点放在双向链表的头部,如果头插后,我们的缓存大小超过了指定大小, 那么就尾删

hash<Integer,DoubleLinkde>, 存key和key-value组成的双链表的结点, DoubleLinkde是我们自定义的内部类来模拟双链表

我们每次查找一个key, 如果在hash表里面能够找到, 那么就把这个结点移动到头部(头插),如果插进去超过大小了, 就尾删, 越靠近后面,访问频次越低.

双链表能够存贮前驱和后继的值, 这样可以很方便进行头插和尾删

代码编写

java 复制代码
class LRUCache {
    private int capacity;// 设置的缓存大小
    private int currentSize;// 当前缓存的大小
    private HashMap<Integer, DoubleLinked> map;// 用哈希表存储key-value
    private DoubleLinked head, tail;// 虚拟头尾结点

    // 双向链表节点类
    private class DoubleLinked {
        int key, value;
        DoubleLinked prev, next;
    // 构造方法
        public DoubleLinked() {}
        public DoubleLinked(int key, int value) {
            this.key = key;
            this.value = value;
        }
    }

    // 构造函数,初始化容量
    public LRUCache(int capacity) {
        this.capacity = capacity;
        this.currentSize = 0;
        map = new HashMap<>();
        
        // 初始化伪头节点和伪尾节点
        head = new DoubleLinked();
        tail = new DoubleLinked();
        head.next = tail;
        tail.prev = head;
    }

    // 获取缓存中的值,如果存在返回值,否则返回-1
    public int get(int key) {
        DoubleLinked node = map.get(key);
        if (node == null) {
            return -1;
        }
        // 访问过该节点,移动到头部
        moveToHead(node);
        return node.value;
    }

    // 插入一个新的键值对
    public void put(int key, int value) {
        DoubleLinked node = map.get(key);

        if (node == null) {
            // 插入新节点
            DoubleLinked newNode = new DoubleLinked(key, value);
            map.put(key, newNode);
            addToHead(newNode);
            currentSize++;

            if (currentSize > capacity) {
                // 超过容量,移除尾部节点
                DoubleLinked tailNode = removeTail();
                map.remove(tailNode.key);
                currentSize--;
            }
        } else {
            // 更新已有节点的值,并移动到头部
            node.value = value;
            moveToHead(node);
        }
    }

    // 将节点添加到头部
    private void addToHead(DoubleLinked node) {
        node.next = head.next;
        node.prev = head;
        head.next.prev = node;
        head.next = node;
    }

    // 将节点移到头部
    private void moveToHead(DoubleLinked node) {
        if (node == null) {
            return;
        }
        removeNode(node);
        addToHead(node);
    }

    // 移除节点
    private void removeNode(DoubleLinked node) {
        if (node == null) {
            return;
        }
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

    // 移除尾部节点
    private DoubleLinked removeTail() {
        if (tail.prev == null) {
            return null;
        }
        DoubleLinked node = tail.prev;
        removeNode(node);
        return node;
    }
}
相关推荐
我是苏苏7 小时前
Redis开发07:使用stackexchange.redis库实现简单消息队列
数据库·redis·缓存
云心雨禅8 小时前
WordPress提速指南:Memcached+Super Static Cache+CDN缓存网站内容
linux·服务器·数据库·缓存·memcached
大刘讲IT10 小时前
下一代固态硬盘引入HBM缓存技术的深度可行性分析
人工智能·经验分享·缓存·ai
闲蛋小超人笑嘻嘻10 小时前
HTTP缓存:强缓存和协商缓存
缓存
June`17 小时前
Redis:高性能内存数据库的六大核心优势
数据库·redis·缓存
一條狗20 小时前
学习日报 20250929|缓存击穿及其解决方案
redis·缓存
麦兜*20 小时前
Redis高可用架构设计:主从复制、哨兵、Cluster集群模式深度对比
java·数据库·spring boot·redis·spring·spring cloud·缓存
王嘉俊92520 小时前
Redis 入门:高效缓存与数据存储的利器
java·数据库·redis·后端·spring·缓存·springboot
Vahala0623-孔勇21 小时前
MyBatis缓存架构深度拆解:从PerpetualCache的LRU陷阱到Redis分布式二级缓存防穿透实战
缓存·架构·mybatis
kimi7041 天前
HTTP(web缓存与历史迭代)
缓存