LFU Cache

2018-11-06 20:06:04

LFU(Least Frequently Used)算法根据数据的历史访问频率来淘汰数据,其核心思想是“如果数据过去被访问多次,那么将来被访问的频率也更高”。

如何高效的实现一个LFU Cache是一个难点,其实现方式要比LRU要复杂一点,问题的核心就是如果对不同的freq进行计数和维护。这里用到的思路和最大频率栈是类似的,也就是对每个freq都开辟一个Set来进行单独的维护。

为了实现的方便,我们可以在每个freq节点中放入一个LinkedHashSet,这样就可以很方便的进行编码。

public class LFUCache {
    private FreqNode head;
    private FreqNode tail;
    private HashMap<Integer, Integer> key2val;
    private HashMap<Integer, FreqNode> key2node;
    private int capacity;

    public LFUCache(int capacity) {
        this.capacity = capacity;
        this.head = new FreqNode(0);
        this.tail = new FreqNode(Integer.MAX_VALUE);
        this.key2val = new HashMap<>();
        this.key2node = new HashMap<>();
        head.next = tail;
        tail.prev = head;
    }

    public int get(int key) {
        if (capacity == 0) return -1;
        if (key2val.containsKey(key)) {
            int res = key2val.get(key);
            increaseFreq(key);
            return res;
        }
        return -1;
    }

    public void put(int key, int value) {
        if (capacity == 0) return;
        if (key2val.containsKey(key)) {
            key2val.put(key, value);
            increaseFreq(key);
        }
        else {
            maintainSize();
            key2val.put(key, value);
            key2node.put(key, head);
            head.keys.add(key);
            increaseFreq(key);
        }
    }

    private void increaseFreq(int key) {
        FreqNode cur = key2node.get(key);
        FreqNode next = null;
        if (cur.next.freq == cur.freq + 1) {
            next = cur.next;
        }
        else {
            next = new FreqNode(cur.freq + 1);
            next.next = cur.next;
            cur.next.prev = next;
            cur.next = next;
            next.prev = cur;
        }
        next.keys.add(key);
        cur.keys.remove(key);
        key2node.put(key, next);
        if (cur.keys.size() == 0 && cur != head) delete(cur);
    }

    private void maintainSize() {
        if (key2val.size() >= capacity) {
            FreqNode cur = head.next;
            Iterator<Integer> iter = cur.keys.iterator();
            int key = iter.next();
            key2val.remove(key);
            key2node.remove(key);
            cur.keys.remove(key);
            if (cur.keys.size() == 0) delete(cur);
        }
    }

    private void delete(FreqNode node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }
}

class FreqNode {
    public int freq;
    public FreqNode prev ;
    public FreqNode next;
    public LinkedHashSet<Integer> keys;

    public FreqNode(int freq) {
        this.freq = freq;
        prev = null;
        next = null;
        keys = new LinkedHashSet<>();
    }
}

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

猜你喜欢

转载自www.cnblogs.com/TIMHY/p/9917674.html
LFU