力扣第146题-LRU 缓存

396 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第13天,点击查看活动详情

前言

力扣第146题 LRU 缓存 如下所示:

image.png

一、思路

题目很长,但题目的目的很简单:自己实现一个满足 LRU 的数据结构。LRU缓存机制是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。

此外题目中还有一个要求:get 和 put 操作都需要满足 O(1) 的时间复杂度

如何实现这一要求呢?我么在这里将 getput 操作分开看。

  • get操作:为了满足取元素时,时间复杂度为 O(1)。可以使用一个哈希表 HashMap 来存储所有最近使用的元素。如果哈希表中存在 key 则返回对应的 val 即可
  • put 操作会有插入和更新两种情况,为了在插入和更新时都能有 O(1) 的时间复杂度,我们可以使用双向链表来存储最近的元素。
    • 插入:插入时需要判断当前缓存的大小是否会溢出,如不溢出则直接插入到头部即可,如溢出则移除最后一个元素后,再将元素插入到头部
    • 更新:更新元素的值后,将当前元素移动到头部

双向链表

假设有如下的双向链表,有 a, b, c 三个节点:

image.png

我们缓存的大小设置为 3,也就是说双向链表中存储的元素不要超过 3

  1. 更新

假设我们需要更新 b 节点,用黄色标识。

image.png

我们现在双向链表中移除节点 b

image.png

然后再将 b 放到双向链表的头部

image.png

  1. 插入新元素

假设插入的元素为 d,我们先移除双向链表中的最后一个元素 c

image.png

再将 d 放到双向链表的头部即可

image.png

二、实现

实现代码

实现代码中主要就是需要自己手写一个双向链表 DoubleLinkNode,此外要注意,最近使用的元素放到链表的头部。

public class LRUCache {
    class DoubleLinkNode {
        int key;
        int val;
        DoubleLinkNode before;
        DoubleLinkNode next;
        public DoubleLinkNode() {}
        public DoubleLinkNode(int key, int val) {
            this.key = key;
            this.val = val;
        }
    }

    private Map<Integer, DoubleLinkNode> map = new HashMap<>();
    private int size;
    private int capacity;
    private DoubleLinkNode head, tail;

    // 初始化
    public LRUCache(int capacity) {
        this.size = 0;
        this.capacity = capacity;
        // 前置和后置节点
        head = new DoubleLinkNode();
        tail = new DoubleLinkNode();
        head.next = tail;
        tail.before = head;
    }

    public int get(int key) {
        DoubleLinkNode node = map.get(key);
        if (node == null) {
            return -1;
        }
        moveToHead(node);
        return node.val;
    }

    public void put(int key, int value) {
        DoubleLinkNode node = map.get(key);
        if (node == null) {
            if (size == capacity) {
                // 移除尾部节点
                DoubleLinkNode tail = removeTail();
                map.remove(tail.key);
                size--;
            }
            DoubleLinkNode newNode = new DoubleLinkNode(key, value);
            map.put(key, newNode);
            addToHead(newNode);
            ++size;
        }
        else {
            // 移动到头部
            node.val = value;
            moveToHead(node);
        }
    }

    private void addToHead(DoubleLinkNode node) {
        node.before = head;
        node.next = head.next;
        head.next.before = node;
        head.next = node;
    }

    private void removeNode(DoubleLinkNode node) {
        node.before.next = node.next;
        node.next.before = node.before;
    }

    private void moveToHead(DoubleLinkNode node) {
        removeNode(node);
        addToHead(node);
    }

    private DoubleLinkNode removeTail() {
        DoubleLinkNode res = tail.before;
        removeNode(res);
        return res;
    }
}

测试代码

本题无测试代码

结果

image.png

三、总结

感谢看到最后,非常荣幸能够帮助到你~♥

如果你觉得我写的还不错的话,不妨给我点个赞吧!如有疑问,也可评论区见~