146. LRU 缓存机制

180 阅读2分钟

Solution:维护一个HashMap和一个DoubleLinkedList

  • 实现get()可以用HashMap,复杂度O(1)
  • 可以用一个队列LinkedList来实现“顺序”。
  • 在put()添加元素时,有addToFirst和remove这两个操作。LinkedList添加到头部复杂度为O(1)。remove为O(N),因为节点不知道前一个节点。
  • 因此,使用DoubleLinkedList,可以满足remove的O(1),因为每个节点都知道pre和next。

/**
 * 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);
 */
 
class LRUCache {
    class Node {
        int key, value;
        Node pre, next;
        public Node(int key, int value) {
            this.key = key;
            this.value = value;
        }
        public Node() {}
    }

    int capacity;
    int count = 0;
    Node head, tail;
    Map<Integer, Node> map;

    public LRUCache(int capacity) {
        this.capacity = capacity;
        this.head = new Node();
        this.tail = new Node();
        head.next = tail;
        tail.pre = head;
        map = new HashMap<>();
    }
    // +---------+       +---------+
    // |         | ————> |         |
    // |dummyHead|       |dummyTail|
    // |         | <———— |         |
    // +---------+       +---------+
    
    public int get(int key) {
        // 需要判断,不存在的key会返回null
        if (map.containsKey(key)) {
            updateNode(map.get(key)); //用过,更新到头部
            return map.get(key).value;
        } else {
            return -1; //查无
        }
    }
    
    public void put(int key, int value) {
        if (map.containsKey(key)) { // 已存在,更新value, 放到头部
            map.get(key).value = value;
            updateNode(map.get(key));
        } else {
            Node node = new Node(key, value);
            map.put(key, node);
            addNodeToFirst(node);
            if (count > capacity) { //超过容量了
                map.remove(tail.pre.key); //顺序不能反
                removeNode(tail.pre);
                
            }
        }
    }

    //更新某个节点:把它挪到头部,即先删后加
    public void updateNode(Node node) {
        removeNode(node);
        addNodeToFirst(node);
    }

    //把一个node放到DLL的头部
    public void addNodeToFirst(Node node) {
        node.pre= head;
        node.next = head.next;
        head.next.pre = node;
        head.next = node;
        count++; //更新当前DLL节点数量
    }

    //删除某个节点
    public void removeNode(Node node) {
        node.pre.next = node.next;
        node.next.pre = node.pre;
        count--; //更新当前DLL节点数量
    }
    
    //resize
    public void resize(int newCapacity) {
        this.capacity = newCapacity;

        // Evict from the tail if needed
        while (count > capacity) {
            Node toRemove = tail.pre;
            map.remove(toRemove.key);
            removeNode(toRemove);
        }
    }

}

How to make it thread safe?

public synchronized int get(int key) {
    if (map.containsKey(key)) {
        updateNode(map.get(key));
        return map.get(key).value;
    } else {
        return -1;
    }
}

public synchronized void put(int key, int value) {
    if (map.containsKey(key)) {
        map.get(key).value = value;
        updateNode(map.get(key));
    } else {
        Node node = new Node(key, value);
        map.put(key, node);
        addNodeToFirst(node);
        if (count > capacity) {
            map.remove(tail.pre.key);
            removeNode(tail.pre);
        }
    }
}

public synchronized void resize(int newCapacity) {
    this.capacity = newCapacity;
    while (count > capacity) {
        Node toRemove = tail.pre;
        map.remove(toRemove.key);
        removeNode(toRemove);
    }
}