146、LRU

为了满足LRU缓存的约束并保证getput操作的时间复杂度为O(1),我们可以使用哈希表加双向链表的数据结构。哈希表用于快速定位一个节点,双向链表用于维护节点的使用顺序,最近使用的节点被放到链表尾部,最久未使用的节点在链表头部。
146、LRU
以下是一个简单的实现:

import java.util.HashMap;

class LRUCache {

    class DLinkedNode {
        int key;
        int value;
        DLinkedNode prev;
        DLinkedNode next;
    }

    private void addNode(DLinkedNode node) {
        // Always add the new node right after head.
        node.prev = head;
        node.next = head.next;

        head.next.prev = node;
        head.next = node;
    }

    private void removeNode(DLinkedNode node){
        // Remove an existing node from the linked list.
        DLinkedNode prev = node.prev;
        DLinkedNode next = node.next;

        prev.next = next;
        next.prev = prev;
    }

    private void moveToHead(DLinkedNode node){
        // Move certain node in between to the head.
        removeNode(node);
        addNode(node);
    }

    private DLinkedNode popTail() {
        // Pop the current tail.
        DLinkedNode res = tail.prev;
        removeNode(res);
        return res;
    }

    private HashMap<Integer, DLinkedNode> cache = new HashMap<>();
    private int size;
    private int capacity;
    private DLinkedNode head, tail;

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

        head = new DLinkedNode();
        // head.prev = null;

        tail = new DLinkedNode();
        // tail.next = null;

        head.next = tail;
        tail.prev = head;
    }

    public int get(int key) {
        DLinkedNode node = cache.get(key);
        if (node == null) return -1;

        // move the accessed node to the head;
        moveToHead(node);

        return node.value;
    }

    public void put(int key, int value) {
        DLinkedNode node = cache.get(key);

        if(node == null) {
            DLinkedNode newNode = new DLinkedNode();
            newNode.key = key;
            newNode.value = value;

            cache.put(key, newNode);
            addNode(newNode);

            ++size;

            if(size > capacity) {
                // pop the tail
                DLinkedNode tail = popTail();
                cache.remove(tail.key);
                --size;
            }
        } else {
            // update the value.
            node.value = value;
            moveToHead(node);
        }
    }
}

这个实现中,DLinkedNode类是双向链表的节点,包含键、值以及指向前一个节点和后一个节点的指针。LRUCache类中包含了一个哈希表cache来存储键和对应节点的映射,以及一个虚拟的双向链表(由headtail节点作为哨兵节点)来维护所有节点的使用顺序。

  • addNode方法将新节点添加到双向链表的头部(即head节点之后),表示这个节点是最近使用的。
  • removeNode方法从双向链表中移除一个节点。
  • moveToHead方法将一个节点移动到双向链表的头部,表示这个节点最近被访问过。
  • popTail方法移除双向链表尾部的节点,即最久未使用的节点,并返回这个节点。

get方法首先查找哈希表,如果找到了对应的节点,则将其移动到双向链表的头部,并返回节点的值。如果未找到,则返回-1。

put方法首先查找哈希表,如果未找到对应的节点,则创建一个新节点并添加到双向链表的头部和哈希表中。如果当前缓存的大小超过了容量限制,则移除双向链表尾部的节点,并从哈希表中移除对应的键。如果找到了对应的节点,则更新节点的值,并将其移动到双向链表的头部。

posted @ 2024-07-19 11:03  economies  阅读(28)  评论(0)    收藏  举报