只个代码由LRU改进得到。
如果每一个频率放在一个LRU里面,每个LRU也有头尾两个指针,指向相邻的LRU。
实际上相邻的LRU可以由frequency = t+1的第一node,可以由frequency = t的最后一个唯一确认。
也就是说,在LRU的设计基础上。我们再多记录一个finalNodes,记录每种频率的尾部就好了。

代码因为情况较多,所以要分析好了,才能归并。

频率可以不连续,最小频率也不一定为1. 我们put(1,1), get(1) LFU里现在只有这个点,频率为2,最小频率不是1.
我们在put(2,2), get(2),get(2), get(2). 2出现了4次。 也就是说频率为2的点1, 指向的下一个是频率为4的点2.

移除node和LRU一样。

添加node,

  1. map里不存在的要加到头部。
    2.1 map里存在的点,加到freq + 1尾部的后一个。

2.2 如果刚好freq+1这个频率不存在(也就是freq+1在finalNodes里没有,我们就不需要移动。)

public class LFUCache {
    private int capacity;
    private int count;
    private HashMap<Integer, Tuple> map1; // whether appeared
    private HashMap<Integer, Tuple> finalNodes; // value : the final node of key times
    private Tuple dummyHead;
    private Tuple dummyEnd;
    
    public LFUCache(int capacity) {
        this.capacity = capacity;
        count = 0;
        map1 = new HashMap<Integer, Tuple>();
        finalNodes = new HashMap<>();
        dummyHead = new Tuple(0, 0, 0);
        dummyEnd = new Tuple(0, 0, 0);
        dummyHead.next = dummyEnd;
        dummyEnd.prev = dummyHead;
    }
    
    public int get(int key) {
        if (capacity == 0 || !map1.containsKey(key)) {
            return -1;
        }
        Tuple old = map1.get(key);
        put(key, old.value);
        return old.value;
    }
    
    public void put(int key, int value) {
        if (capacity == 0) {
            return;
        }
        if (map1.containsKey(key)) { // this key has appeared
            Tuple cur = map1.get(key);
            if (finalNodes.get(cur.times) == cur && finalNodes.get(cur.times + 1) == null) { // the position should not change
                finalNodes.put(cur.times, cur.prev.times == cur.times ? cur.prev : null);
                cur.times++;
                cur.value = value;
                finalNodes.put(cur.times, cur);
                return;
            }
            removeNode(cur); // remove node cur
            if (finalNodes.get(cur.times) == cur) {
                finalNodes.put(cur.times, cur.prev.times == cur.times ? cur.prev : null);
            }
            cur.times++;
            cur.value = value;
            Tuple finalNode = finalNodes.get(cur.times) == null ? finalNodes.get(cur.times - 1) : finalNodes.get(cur.times);
            insertNode(finalNode, cur); 
            finalNodes.put(cur.times, cur); 
        } else {
            if (count == capacity) { // reach limt of the cache
                Tuple head = dummyHead.next;
                removeNode(head); //remove the first which appeared least times and is the least Used
                map1.remove(head.key);
                if (finalNodes.get(head.times) == head) {
                    finalNodes.remove(head.times);
                }
            } else {
                count++;
            } 
            insertHead(key, value);
        } 
    }
    
    public void insertHead(int key, int value) {
        Tuple cur = new Tuple(key, value, 1);
            if (finalNodes.get(1) == null) {
                insertNode(dummyHead, cur);
            } else {
                Tuple finalNode = finalNodes.get(1);
                insertNode(finalNode, cur);
            }
            finalNodes.put(1, cur);
            map1.put(key, cur);
    }
    
    public void insertNode(Tuple t1, Tuple t2) {
        t2.next = t1.next;
        t1.next.prev = t2;
        t1.next = t2;
        t2.prev = t1;
    }
    
    public void removeNode(Tuple node) {
        node.next.prev = node.prev;
        node.prev.next = node.next;
    }
    
    class Tuple {
        int key;
        int value;
        int times;
        Tuple prev;
        Tuple next;
        public Tuple(int key, int value, int times) {
            this.key = key;
            this.value = value;
            this.times = times;
        }
    }
}

/**
 * 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);
 */

大米中的大米
12 声望5 粉丝

你的code是面向面试编程的,收集和整理leetcode discussion里个人认为的最优且最符合我个人思维逻辑的解法。