🌕🌗 460. LFU 缓存

吞佛童子2022年10月10日
  • algorithm
  • list
大约 2 分钟

🌕🌗 460. LFU 缓存

难度: 🌕🌗

问题描述

img_5.png


解法

class LFUCache {
    int size; // 节点数
    int capacity; // 容量
    HashMap<Integer, Node> map; // 根据 key 快速定位到节点
    // 根据 fre 快速定位到当前 fre 下的链表,借助 LinkedList 快速删除首尾节点
    // 实际上,当 LinkedList 节点数过多时,要删除其中某个元素,复杂度不止 1
    // 插入节点放入尾部,移除节点从头结点开始
    HashMap<Integer, LinkedList<Node>> freMap; 
    int minFre; // 记录最小 fre 便于快速定位到该 fre 下的链表

    public LFUCache(int capacity) {
        this.size = 0;
        this.capacity = capacity;
        this.map = new HashMap<>();
        this.freMap = new HashMap<>();
        this.minFre = 0;
    }
    
    public int get(int key) {
        if(capacity == 0 || !map.containsKey(key)) {
            return -1;
        }
        Node select = map.get(key);
        // get() 调用一次,fre ++,移到新的对应频次的列表中
        int oldFre = select.fre;
        LinkedList<Node> oldList = freMap.get(oldFre);
        oldList.remove(select); // 从旧链表中删除该节点
        freMap.put(oldFre, oldList); // 更新 freMap
        // 更新 minFre
        if(oldList.size() == 0 && minFre == oldFre) {
            minFre = oldFre + 1;
        }        
        int newFre = oldFre + 1;
        select.fre = newFre;
        // 判断新频次下的链表是否存在
        if(!freMap.containsKey(newFre)) {
            LinkedList<Node> tmpList = new LinkedList<>();
            freMap.put(newFre, tmpList);
        }
        LinkedList<Node> newList = freMap.get(newFre);
        newList.addLast(select);
        freMap.put(newFre, newList);
        return select.val;
    }
    
    public void put(int key, int value) {
        // System.out.println(key + "  " + minFre);
        if(capacity == 0) {
            return;
        }
        if(map.containsKey(key)) {
            Node select = map.get(key);
            select.val = value;
            get(key);
        } else {
            // 判断容量是否足够
            if(size == capacity) {
                // 需要先移除最小频次下的首个节点
                LinkedList<Node> oldestList = freMap.get(minFre);
                Node rv = oldestList.get(0);
                oldestList.removeFirst();
                size --;
                map.remove(rv.key);
            }
            // 更新 minFre - 既然新插入了一个节点,那么 minFre 必 == 1
            minFre = 1;
            Node insert = new Node(key, value);
            if(!freMap.containsKey(1)) {
                LinkedList<Node> tmpList = new LinkedList<>();
                freMap.put(1, tmpList);
            }
            LinkedList<Node> insertList = freMap.get(1);
            insertList.addLast(insert);
            size ++;
            map.put(key, insert);
            freMap.put(1, insertList);
        }
    }
}

// 构造双向链表节点类
class Node {
    int key;
    int val;
    int fre;
    Node next;
    Node prev;
    public Node(int key, int val) {
        this.key = key;
        this.val = val;
        this.fre = 1;
    }
}

/**
 * Your LFUCache object will be instantiated and called as such:
 * LFUCache obj = new LFUCache(capacity);
 * int param_1 = obj.get(key);
 * obj.put(key,value);
 */

输出

img_4.png

上次编辑于: 2022/10/10 下午8:43:48
贡献者: liuxianzhishou