【LeetCode-中等题】146. LRU 缓存

题目

LRU缓存是什么:LRU缓存机制,你想知道的这里都有
实现 LRU 缓存算法
在这里插入图片描述

方法一:直接继承LinkedHashMap调用api

class LRUCache extends LinkedHashMap<Integer, Integer>{
    
    
    private int capacity;
    
    public LRUCache(int capacity) {
    
    
        super(capacity, 0.75F, true);
        this.capacity = capacity;
    }

    public int get(int key) {
    
    
        return super.getOrDefault(key, -1);
    }

    public void put(int key, int value) {
    
    
        super.put(key, value);
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
    
    
        return size() > capacity; 
    }
}

方法二:自定义LinkedHashMap = HashMap + ListNode == LinkedHashMap

map —>key存 integer value存链表节点
ListNode 存 next 和prev 引用 以及 存 key 和value 具体值
在这里插入图片描述

图解:官方图解

步骤:

  1. 定义一个自定义的双向链表节点类 DLinkedNode,该类包含 key 和 value 字段,并且具有 prev 和 next 指针用于构建双向链表。
  2. 创建一个哈希表 cache 用于存储键值对,其中键为索引,值为双向链表的节点。
  3. 定义变量 size 表示当前哈希表中已存储的键值对数量。
  4. 定义变量 capacity 表示哈希表的容量。
  5. 创建伪头部节点 head 和伪尾部节点 tail,并将它们连接起来形成一个空的双向链表。

方法列表:

  1. 构造函数 LRUCache(int capacity):初始化缓存的容量,同时初始化 size、capacity、head 和 tail 变量。
  2. int get(int key):获取指定键对应的值,如果键不存在则返回 -1。如果键存在,则通过哈希表定位到双向链表中的节点,并将该节点移到链表头部,然后返回节点的值。
  3. void put(int key, int value):插入或更新键值对。如果键已存在,则更新对应的值,并将对应的节点移到链表头部。如果键不存在,则创建一个新的节点,添加到哈希表和链表头部。如果插入后超出容量,则删除尾部节点,并从哈希表中移除对应的记录。
  4. DLinkedNode removeTail():在超出容量时删除尾部节点,并返回删除的尾部节点。
  5. void addToHead(DLinkedNode newNode):将指定节点添加到伪头部节点的后面。
  6. void moveToHead(DLinkedNode node):将指定节点移到伪头部节点的后面,即删除原位置的节点并将其添加到伪头部节点后面。
  7. void removeNode(DLinkedNode node):从双向链表中删除指定节点。
class LRUCache {
    
    
    /**
     * 自定义双向链表
     */
    class DLinkedNode {
    
    
        int key;
        int value;
        DLinkedNode prev;
        DLinkedNode next;
        public DLinkedNode() {
    
    }
        public DLinkedNode(int _key, int _value) {
    
    key = _key; value = _value;}
    }

    private Map<Integer, DLinkedNode> cache = new HashMap<Integer, DLinkedNode>();//定义哈希表  key为索引  value为双向链表的节点
    private int size;//map的存值后的大小
    private int capacity;//map 的容量
    private DLinkedNode head, tail; //定义双向链表的伪头部和伪尾部节点



    /**
     * 定义初始容量方法
     * @param capacity
     */
    public LRUCache(int capacity) {
    
     //定义初始容量方法
        this.size = 0;
        this.capacity = capacity;
        // 使用伪头部和伪尾部节点
        head = new DLinkedNode();
        tail = new DLinkedNode();
        head.next = tail;
        tail.prev = head;

    }

    /**
     * 依据key获取对应value
     * @param key
     * @return
     */
    public int get(int key) {
    
    
        DLinkedNode node = cache.get(key);//获取key对应的链表节点
        if (node == null) {
    
      //如果node为null  说明key没有对应的value值
            return  -1;
        }
        // 如果 key 存在,先通过哈希表定位,再移到头部
        moveToHead(node);//将待get的节点移动到头部,再返回节点的值
        return node.value;
    }

    /**
     * 往哈希表中添加元素的方法
     * @param key
     * @param value
     */
    public void put(int key, int value) {
    
    
        DLinkedNode node = cache.get(key);//put时  先获取这个key的位置有没有值  有?覆盖原值  没有就插入进去
        if (node == null) {
    
    // 如果 key 不存在,创建一个新的节点
            DLinkedNode newNode = new DLinkedNode(key, value);
            // 添加进哈希表
            cache.put(key, newNode);
            // 添加至双向链表的头部
            addToHead(newNode);
            size++; //map集合元素+1
            if (size > capacity) {
    
    
                // 如果超出容量,删除双向链表的尾部节点
                DLinkedNode tail = removeTail(); //从链表中把尾结点删除,并且方法得返回这个节点  方便map把这个节点对应的元素remove掉
                cache.remove(tail.key);
                size--;
            }
        }else{
    
    // 如果 key 存在,覆盖原值,并移到链表头部
            node.value = value;
            moveToHead(node);
        }
    }

    /**
     * 超出容量时删除尾部节点 并且返回尾部节点  方便对清除map中的残留
     * @return
     */
    private DLinkedNode removeTail() {
    
    
        DLinkedNode tailNode = tail.prev;//获取伪尾节点的前一个节点  即链表真实的尾节点
        removeNode(tailNode);
        return tailNode;//返回尾部节点  方便对清除map中的残留
    }


    /**
     * 新增节点时节点添加到伪头结点后面的位置
     * @param newNode
     */
    private void addToHead(DLinkedNode newNode) {
    
    
        newNode.prev = head;
        newNode.next = head.next;
        head.next.prev = newNode;
        head.next = newNode;
    }

    /**
     * get方法获取key对应value
     * put方法出现重复key时  覆盖原value
     * 这两种情况都会触发将操作的节点移动到伪首节点的后面
     * @param node
     */
    private void moveToHead(DLinkedNode node) {
    
    
        //删除原位置的自己
        removeNode(node);
        //将自己添加到伪首节点的后面
        addToHead(node);//调用上面写过的将节点添加到伪首节点的后面
    }

    /**
     * 删除node节点
     * @param node
     */
    private void removeNode(DLinkedNode node) {
    
    
        node.prev.next = node.next;
        node.next.prev = node.prev;
        node.next = null;
        node.prev = null;
    }

}

猜你喜欢

转载自blog.csdn.net/weixin_45618869/article/details/132563416