Problem_LRU, LFU - xwu36/LeetCode GitHub Wiki

Problem 460. LFU Cache

Problem:

Design and implement a data structure for Least Frequently Used (LFU) cache. It should support the following operations: get and put.

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
put(key, value) - Set or insert the value if the key is not already present. When the cache reaches its capacity, it should invalidate the least frequently used item before inserting a new item. For the purpose of this problem, when there is a tie (i.e., two or more keys that have the same frequency), the least recently used key would be evicted.

Follow up:
Could you do both operations in O(1) time complexity?

Example:

LFUCache cache = new LFUCache( 2 /* capacity */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // returns 1
cache.put(3, 3);    // evicts key 2
cache.get(2);       // returns -1 (not found)
cache.get(3);       // returns 3.
cache.put(4, 4);    // evicts key 1.
cache.get(1);       // returns -1 (not found)
cache.get(3);       // returns 3
cache.get(4);       // returns 4

Algorithm:

class LFUCache {
    private HashMap<Integer, Integer> vals;
    private HashMap<Integer, Integer> counts;
    private HashMap<Integer, LinkedHashSet<Integer>> lists;
    private int cap;
    private int min = -1;
    public LFUCache(int capacity) {
        this.vals = new HashMap<>();
        this.counts = new HashMap<>();
        this.lists = new HashMap<>();
        this.cap = capacity;
    }
    
    public int get(int key) {
        if(vals.get(key) == null || cap == 0)
            return -1;
        int val = vals.get(key);
        put(key, val);
        return val;
    }
    
    public void put(int key, int value) {
        if(cap == 0) return;
        if(vals.get(key) != null){
            int count = counts.get(key);
            lists.get(count).remove(key);
            counts.put(key, count + 1);
            vals.put(key, value);
            if(min == count && lists.get(count).size() == 0){
                min++;
            }
            if(lists.get(count + 1) == null)
                lists.put(count + 1, new LinkedHashSet<Integer>());
            lists.get(count + 1).add(key);
        }else{
            vals.put(key, value);
            counts.put(key, 1);
            if(vals.size() > cap){
                int first = lists.get(min).iterator().next();
                vals.remove(first);
                counts.remove(first);
                lists.get(min).remove(first);
            }
            if(lists.get(1) == null)
                lists.put(1, new LinkedHashSet<Integer>());
            lists.get(1).add(key);
            min = 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);
 */

146. LRU Cache

Problem:

Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and put.

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
put(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.

Follow up:
Could you do both operations in O(1) time complexity?

Example:

LRUCache cache = new LRUCache( 2 /* capacity */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // returns 1
cache.put(3, 3);    // evicts key 2
cache.get(2);       // returns -1 (not found)
cache.put(4, 4);    // evicts key 1
cache.get(1);       // returns -1 (not found)
cache.get(3);       // returns 3
cache.get(4);       // returns 4

Algorithm:

class LRUCache {
    private Map<Integer, DoubleLinkedNode> map;
    private int capacity;
    private DoubleLinkedNode head;
    private DoubleLinkedNode tail;
    public LRUCache(int capacity) {
        this.map = new HashMap<>();
        this.capacity = capacity;
        this.head = new DoubleLinkedNode();
        this.tail = new DoubleLinkedNode();
        head.next = tail;
        tail.pre = head;
    }
    
    public int get(int key) {
        if(map.get(key) == null)
            return -1;
        int val = map.get(key).val;
        put(key, val);
        return val;
    }
    
    public void put(int key, int value) {
        if(map.get(key) != null){
            map.get(key).val = value;
            moveToHead(map.get(key));
        }else{
            DoubleLinkedNode tmp = new DoubleLinkedNode(key, value);
            addToHead(tmp);
            map.put(key, tmp);
            if(map.size() > capacity){
                int last = removeNode(tail.pre);
                map.remove(last);
            }
        }
    }
    
    public void moveToHead(DoubleLinkedNode node){
        removeNode(node);
        addToHead(node);
    }
    
    public void addToHead(DoubleLinkedNode node){
        node.next = head.next;
        head.next.pre = node;
        head.next = node;
        node.pre = head;
    }
    
    public int removeNode(DoubleLinkedNode node){
        int key = node.key;
        node.pre.next = node.next;
        node.next.pre = node.pre;
        return key;
    }
    
    class DoubleLinkedNode{
        DoubleLinkedNode pre;
        DoubleLinkedNode next;
        int key;
        int val;
        DoubleLinkedNode(){};
        DoubleLinkedNode(int key, int val){
            this.key = key;
            this.val = val;
        }
    }
}

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache obj = new LRUCache(capacity);
 * int param_1 = obj.get(key);
 * obj.put(key,value);
 */
⚠️ **GitHub.com Fallback** ⚠️