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;
    }
}
相关推荐
fengxin_rou16 小时前
Redis 从零到精通:第一篇 初识redis
数据库·redis·缓存
陌上丨19 小时前
Redis内存使用率在95%以上,请问是什么原因?如何解决?
数据库·redis·缓存
dawdo22220 小时前
自己动手从头开始编写LLM推理引擎(9)-KV缓存实现和优化
缓存·llm·transformer·qwen·kv cache
小北方城市网21 小时前
RabbitMQ 生产级实战:可靠性投递、高并发优化与问题排查
开发语言·分布式·python·缓存·性能优化·rabbitmq·ruby
陌上丨21 小时前
什么是Redis的大Key和热Key?项目中一般是怎么解决的?
数据库·redis·缓存
小园子的小菜21 小时前
深入剖析HBase HFile原理:文件结构、Block协作与缓存机制
数据库·缓存·hbase
廋到被风吹走1 天前
【缓存优化】缓存穿透:布隆过滤器(Guava/RedisBloom)
缓存·guava
Moshow郑锴1 天前
Spring Boot Data API 与 Redis 集成:KPI/图表/表格查询的缓存优化方案
spring boot·redis·缓存
小马爱打代码1 天前
MyBatis:缓存体系设计与避坑大全
java·缓存·mybatis
三水不滴1 天前
SpringBoot+Caffeine+Redis实现多级缓存
spring boot·redis·笔记·缓存