LeetCode-146. LRU 缓存-Java-medium

题目链接

法一(LinkedHashMap)
/**
 * 法一
 * LinkedHashMap
 * (1)定义:继承自HashMap,在HashMap基础上,通过维护一条双向链表,解决了HashMap不能随时保持遍历顺序和插入顺序一致的问题
 * (2)数据结构:HashMap + 双向链表
 * (3)使用场景:当我们希望有顺序地去存储key-value时,就需要使用LinkedHashMap了,例如LRU缓存过期策略
 * (4)初始化参数:
 *     LinkedHashMap<Integer, Integer> cache = new LinkedHashMap<Integer, Integer>(capacity, 0.75f, true);
 *     capacity 初始容量
 *     loadFactor 加载因子
 *     accessOrder,默认false为插入顺序,true为访问顺序
 *     插入顺序:会按照插入顺序(put)进行排序
 *     访问顺序:会按照访问顺序(get和put)进行排序,也就是插入和访问都会将当前节点放置到尾部,尾部代表的是最近访问的数据
 * (5)removeEldestEntry()方法
 *     removeEldestEntry()方法用于检查是否删除Map中最旧的条目
 *     返回值:如果将最旧的条目从映射中删除,则返回true;如果保留该条目,则返回false。
 */
public class Solution146_1 {

    private int capacity; // LRU缓存容量
    private LinkedHashMap<Integer, Integer> cache;

    /**
     * 以正整数作为容量capacity初始化LRU缓存
     *
     * @param capacity
     */
    public Solution146_1(int capacity) {
        this.capacity = capacity;
        cache = new LinkedHashMap<>(capacity, 0.75f, true) {
            @Override
            protected boolean removeEldestEntry(Map.Entry eldest) { // 重载removeEldestEntry
                return cache.size() > capacity;
            }
        };
    }

    /**
     * 如果关键字key存在于缓存中,则返回关键字的值,否则返回-1
     *
     * @param key
     * @return
     */
    public int get(int key) {
        return cache.getOrDefault(key, -1);
    }

    /**
     * 如果关键字key已经存在,则变更其数据值value; 如果不存在,则向缓存中插入该组key-value
     * 如果插入操作导致关键字数量超过capacity,则应该逐出最久未使用的关键字
     *
     * @param key
     * @param value
     */
    public void put(int key, int value) {
        cache.put(key, value);
    }

}
法二(双向链表 + HashMap)
/**
 * 法二(双向链表 + HashMap)
 * Java的静态内部类
 * 如果一个类要被声明为static的,只有一种情况,就是静态内部类。
 * 静态内部类实际上与普通类(即类名必须与文件名一样的顶级类)一样,只是静态内部类在某一类的内部定义了而已
 */
public class Solution146_2 {

    private int capacity; // LRU缓存容量
    private int dulSize;  // 双向链表长度
    private DulNode dulHead; // 双向链表头节点(不存数据)
    private Map<Integer, DulNode> mp = new HashMap<>(); // 建立key与双向链表节点的映射

    /**
     * 双向链表静态内部类
     */
    private static class DulNode {
        private int key, val;
        private DulNode prev, next;

        // 无参构造函数
        private DulNode() {
        }

        // 有参构造函数
        private DulNode(int key, int val) {
            this.key = key;
            this.val = val;
        }

        // 移除当前节点并返回
        private DulNode remove() {
            prev.next = next;
            next.prev = prev;
            next = null;
            prev = null;
            return this;
        }

        // 将dulNode插入到当前节点后面
        private void insert(DulNode dulNode) {
            dulNode.next = next;
            dulNode.prev = this;
            next.prev = dulNode;
            next = dulNode;
        }
    }

    /**
     * 以正整数作为容量capacity初始化LRU缓存
     *
     * @param capacity
     */
    public Solution146_2(int capacity) {
        this.capacity = capacity;
        dulHead = new DulNode(); // 初始化双向链表头节点
        dulHead.next = dulHead;
        dulHead.prev = dulHead;
    }

    /**
     * 如果关键字key存在于缓存中,则返回关键字的值,否则返回-1
     *
     * @param key
     * @return
     */
    public int get(int key) {
        DulNode dulNode = mp.get(key);
        if (dulNode == null) {
            return -1;
        }
        dulNode = dulNode.remove(); // 从双向链表中移除dulNode
        dulHead.insert(dulNode);    // 将移除的dulNode重新插入到双向链表头节点后面
        return dulNode.val;
    }

    /**
     * 如果关键字key已经存在,则变更其数据值value; 如果不存在,则向缓存中插入该组key-value
     * 如果插入操作导致关键字数量超过capacity,则应该逐出最久未使用的关键字
     *
     * @param key
     * @param value
     */
    public void put(int key, int value) {
        DulNode dulNode = mp.get(key);
        if (dulNode == null) { // key对应节点不存在,则新建一个节点
            dulNode = new DulNode(key, value);
            mp.put(key, dulNode);
            dulSize++;
        } else { // key对应节点存在,则更改该节点的值
            dulNode = dulNode.remove(); // 先移除dulNode
            dulNode.val = value;
        }
        dulHead.insert(dulNode);  // 将dulNode插入到双向链表头节点后面
        if (dulSize > capacity) { // 超出缓存容量
            DulNode dulRemoved = dulHead.prev.remove(); // 移除双向链表最后一个节点(dulHead.prev始终指向最后一个节点)
            mp.remove(dulRemoved.key);
            dulSize--;
        }
    }

}
本地测试
        /**
         * 146. LRU 缓存
         */
        lay.showTitle(146);
        Solution146_1 sol146_1 = new Solution146_1(2);
        sol146_1.put(1, 1); // 缓存是 {1=1}
        sol146_1.put(2, 2); // 缓存是 {1=1, 2=2}
        System.out.print(sol146_1.get(1) + " "); // 返回 1
        sol146_1.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
        System.out.print(sol146_1.get(2) + " "); // 返回 -1 (未找到)
        sol146_1.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
        System.out.print(sol146_1.get(1) + " "); // 返回 -1 (未找到)
        System.out.print(sol146_1.get(3) + " "); // 返回 3
        System.out.println(sol146_1.get(4) + " "); // 返回 4
        Solution146_2 sol146_2 = new Solution146_2(2);
        sol146_2.put(1, 1); // 缓存是 {1=1}
        sol146_2.put(2, 2); // 缓存是 {1=1, 2=2}
        System.out.print(sol146_2.get(1) + " "); // 返回 1
        sol146_2.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
        System.out.print(sol146_2.get(2) + " "); // 返回 -1 (未找到)
        sol146_2.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
        System.out.print(sol146_2.get(1) + " "); // 返回 -1 (未找到)
        System.out.print(sol146_2.get(3) + " "); // 返回 3
        System.out.println(sol146_2.get(4) + " "); // 返回 4
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值