力扣146.LRU缓存(哈希表缓存.映射+双向链表数据结构手搓.维护使用状况顺序)(java)

题目来源

146. LRU 缓存 - 力扣(LeetCode)

代码

java 复制代码
class LRUCache {
    //双向链表结点
    class DLinkedNode {
        int key;
        int value;
        DLinkedNode prev;
        DLinkedNode next;
        DLinkedNode() {};
        DLinkedNode(int key, int value) {this.key = key; this.value = value;}
    }
    private DLinkedNode head, tail; // 虚拟头尾指针

    // 哈希表 做缓存
    private Map<Integer, DLinkedNode> cache = new HashMap<>();
    private int size; //当前缓存大小
    private int capacity; // 缓存容量大小

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

        //创建双向链表
        head = new DLinkedNode();
        tail = new DLinkedNode();
        head.next = tail;
        tail.prev = head;
    }
    
    public int get(int key) {
        DLinkedNode node = cache.get(key);
        //缓存中无对应结点
        if(node == null) return -1; //返回-1
        //有
        moveToHead(node); //更新使用状态顺序
        return node.value; //返回结点的value值
    }
    
    public void put(int key, int value) {
        DLinkedNode node = cache.get(key);
        //缓存中无对应结点 
        if(node == null) {
            // 创建结点插入链表 + 添加至缓存
            node = new DLinkedNode(key, value);
            size ++;

            addToHead(node); //头插法
            cache.put(key, node);

            //检查缓存容量是否足够
            if(size > capacity) {
                // 移除链表尾部元素 + 清理缓存
                DLinkedNode tailNode = removeTail();
                cache.remove(tailNode.key); // 需要尾部结点key
                size --;
            }
        } else {
        // 有
            node.value = value; //修改值
            moveToHead(node); //更新结点使用状态顺序
        }
    }

    // 双向链表基本操作 -> 前插头结点
    private void addToHead(DLinkedNode node) {
        node.prev = head;
        node.next = head.next;

        head.next.prev = node;
        head.next = node;
    }
    // 双向链表基本操作 -> 删除某个结点
    private void removeNode(DLinkedNode node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
        //java会自动识别并释放结点空间
    }
    // 双向链表衍生操作 -> 将结点移到头部更新使用状态(删除结点+前插结点)
    private void moveToHead(DLinkedNode node) {
        removeNode(node);
        addToHead(node);
    }
    // 双向链表衍生操作 -> 删除并返回尾部结点
    private DLinkedNode removeTail() {
        DLinkedNode tailNode = tail.prev;
        removeNode(tailNode);
        return tailNode;
    }
}

/**
 * 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);
 */

代码分析

总的来说,就是 通过哈希表 映射到 双向链表某个结点。双向链表作为实际数据存储结构。

也就是说,写双向链表。

数据结构选择原因

哈希表用于存储数据, 通过key -> 对应数据 。

但是需要考虑到使用状况顺序问题(近期操作时间先后顺序排序),使用双向链表,存储数据。

于是 哈希表中, key -> DLinkedNode(双向链表的结点)

写代码思路

双向链表结点类

创建哈希表

LRU缓存初始化 -> 创建双向链表

LRU缓存的get方法 -> 从哈希表中获取 对应结点

== 需要写 双向链表的更新使用状态顺序的方法。moveToHead

LRU缓存的put方法 -> 从哈希表中获取 对应结点 + 数据插入或修改

数据插入也就是 创建结点,插入结点到链表

== 需要写 双向链表头插法addToHead 方法 and 双向链表删除尾结点方法 removeTail and 双向链表删除某个结点方法 removeNode

数据修改就是对应结点 value值赋值。

相关推荐
ulias2123 分钟前
leetcode热题 - 2
算法·leetcode·职场和发展
牧瀬クリスだ17 分钟前
七大排序一次满足
数据结构·算法·排序算法
liu****17 分钟前
第15届省赛蓝桥杯大赛C/C++大学B组
开发语言·数据结构·c++·算法·蓝桥杯·acm
charlie11451419125 分钟前
嵌入式Linux模块开发——struct module 深度解析:内核模块的核心数据结构
linux·开发语言·数据结构·c
Q741_14734 分钟前
每日一题 3740. 三个相等元素之间的最小距离 I 3741. 三个相等元素之间的最小距离 II 模拟 哈希表 C++ 题解
c++·算法·leetcode·模拟·数组·哈希表
alphaTao43 分钟前
LeetCode 每日一题 2026/4/6-2026/4/12
python·算法·leetcode
独孤--蝴蝶1 小时前
leetcode-动态规划三种问题的异同点
算法·leetcode·动态规划
大卡片1 小时前
IO缓存区
linux·运维·缓存
计算机安禾1 小时前
【数据结构与算法】第37篇:图论(一):图的存储结构(邻接矩阵与邻接表)
数据结构·算法·链表·排序算法·深度优先·图论·visual studio code
sparEE1 小时前
基础排序算法:冒泡、选择、插入、希尔
数据结构·算法·排序算法