LRU cache实现

题目:LRU缓存机制 - LeetCode (中国)
其实现原理可以用一张图来表示:k-v储存在map中,用一个双向链表来维持访问顺序。
为什么要用双向链表?因为可以在O(1)内删除某个节点。
等等,似乎单向链表也可以快速删除,比如《编程之美》有一道题目是从无头的单向链表中快速删除一个节点。为什么在这里为什么不能使用呢?
1)这不是真正的删除,而是替换。真正删除的是下一个节点。
2)这种方法无法“删除”尾节点。

image.png

class LRUCache {
    final int capacity;
    Node head;
    Node tail;
    Map cache = new HashMap<>();


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

        int key;
        int value;
        Node pre;
        Node next;
    }


    public LRUCache(int capacity) {
        this.capacity = capacity;
    }

    public int get(int key) {
        Node node = cache.get(key);
        if (node == null) {
            return -1;
        }
        remove(node);
        setHead(node);
        return node.value;
    }

    private void setHead(Node node) {
        node.pre = null;  // 注释 1
        node.next = null;
  
        if (head == null) {
            head = tail = node;
        } else {
            node.next = head;
            head.pre = node;
            head = node;
        }
    }

    private void remove(Node node) {
        Node before = node.pre;
        Node after = node.next;
        if (before == null) {
            head = after;
        } else {
            before.next = after;
        }

        if (after == null) {
            tail = before;
        } else {
            after.pre = before;
        }
    }


    public void put(int key, int value) {
        Node node = cache.get(key);
        if (node != null) {
            remove(node);
            node.value = value; // 注释3
            setHead(node);
        } else {
            Node newNode = new Node(key, value);
            if (cache.size() >= capacity) {
                cache.remove(tail.key); //  注释 2
                remove(tail);
            }
            setHead(newNode);
            cache.put(key, newNode);
        }
    }
}

我以前没有写过双向链表的代码,所以有兴趣写了写,发现了一些bug。

注释1:从链表中删除元素,除了要保证原链表不能断开之外,还需要考虑到被删除节点的指针往往非空,所以这里需要人为置空。
注释2:当cache处于full状态时,需要删除同时从链表和map中删除改元素,很容易忽略map。而且注释2和下一行的顺序不能交换,因为先执行remove(tail),tail的指针就变化了。
注释3:这里不用生成一个新节点,用newValue替换原值即可。
这道题目还有更方便的实现……本来想引用自己过去的一篇文章,但是知乎的产品设计失去了以前检索文章的功能,只能罢了。

参考:LeetCode - LRU Cache (Java)

你可能感兴趣的:(LRU cache实现)