LRU的设计与实现(算法村第五关黄金挑战)

146. LRU 缓存 - 力扣(LeetCode)

请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。

实现 LRUCache 类:

  • LRUCache(int capacity)正整数 作为容量 capacity 初始化 LRU 缓存
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1
  • void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。

函数 getput 必须以 O(1) 的平均时间复杂度运行。

用一张图来理解整个流程

假设内存只能容纳3个页面,按照 7 0 1 2 0 3 0 4 的次序访问页面。假设内存按照栈的方式来描述访问时间:上面是最近访问的,下面是最远时间访问的,那LRU就是这样工作的:

HashMap + 双向链表

java 复制代码
class LRUCache
{
    /**
     * 双向链表的结点
     */
    class Node
    {
        int key;    //结点的标识
        int value;  //结点的值
        Node pre;
        Node next;

        public Node(){}

        public Node(int key, int value)
        {
            this.key = key;
            this.value = value;
        }
    }

    Node dummyHead; //虚拟头结点
    Node dummyTail; //虚拟尾结点
    /**
     * 用于定位结点的HashMap
     */
    HashMap<Integer, Node> hashMap = new HashMap<>();
    int capacity;   //容量大小
    int size; //当前缓存大小

    public LRUCache(int capacity)
    {
        this.capacity = capacity;
        this.size = 0;

        dummyHead = new Node();
        dummyTail = new Node();
        dummyHead.next = dummyTail;
        dummyTail.pre = dummyHead;
    }

    /**
     * 移除结点
     */
    public void remove(Node node)
    {
        //node前驱的后继改为node的后继
        node.pre.next = node.next;
        //node后继的前驱改为node的前驱
        node.next.pre = node.pre;
    }

    /**
     * 将独立的结点添加到链表头部
     */
    public void addToHead(Node node)
    {
        //头插法
        node.pre = dummyHead;
        node.next = dummyHead.next;
        dummyHead.next.pre = node; //原先表首结点的前驱改为node
        dummyHead.next = node;
    }

    /**
     * 将双向链表中的结点移动到表首
     */
    public void moveToHead(Node node)
    {
        remove(node);
        addToHead(node);
    }


    public int get(int key)
    {
        Node node = hashMap.get(key);

        //如果链表没有这个结点,直接返回-1
        if (node == null)
            return -1;

        //存在的话,先将它提到表首,再返回结点的值
        moveToHead(node);
        return node.value;
    }

    public void put(int key, int value)
    {
        Node node = hashMap.get(key);

        //如果链表没有这个结点,则生成一个新结点,并放在表首
        if(node == null)
        {
            Node newNode = new Node(key, value);
            addToHead(newNode);

            hashMap.put(key, newNode);  //在哈希表中记录这个新结点

            //只有添加新结点的情况下才会时缓存大小变化
            size++;

            //若put之后超出容量,则在链表中删除尾结点,并在哈希表中除名
            if (size > capacity)
            {
                Node tail = dummyTail.pre;
                remove(tail);

                hashMap.remove(tail.key);
            }
        }
        else    //如果已经存在,则将node提到表首,并更新value值
        {
            moveToHead(node);
            node.value = value;
        }
    }
}
相关推荐
badhope4 小时前
Mobile-Skills:移动端技能可视化的创新实践
开发语言·人工智能·git·智能手机·github
码云数智-园园5 小时前
微服务架构下的分布式事务:在一致性与可用性之间寻找平衡
开发语言
C++ 老炮儿的技术栈5 小时前
volatile使用场景
linux·服务器·c语言·开发语言·c++
hz_zhangrl5 小时前
CCF-GESP 等级考试 2026年3月认证C++一级真题解析
开发语言·c++·gesp·gesp2026年3月·gespc++一级
大阿明5 小时前
Spring Boot(快速上手)
java·spring boot·后端
Liu628885 小时前
C++中的工厂模式高级应用
开发语言·c++·算法
bearpping5 小时前
Java进阶,时间与日期,包装类,正则表达式
java
IT猿手5 小时前
基于控制障碍函数的多无人机编队动态避障控制方法研究,MATLAB代码
开发语言·matlab·无人机·openclaw·多无人机动态避障路径规划·无人机编队
邵奈一5 小时前
清明纪念·时光信笺——项目运行指南
java·实战·项目
AI科技星6 小时前
全尺度角速度统一:基于 v ≡ c 的纯推导与验证
c语言·开发语言·人工智能·opencv·算法·机器学习·数据挖掘