460. LFU Cache

https://leetcode.com/problems/lfu-cache/description/

实现LFU,即cache满了后找使用频率最低的移除,如果有多个同最低频率的则移除最后没使用过的。找频率最低的可以用heap或tree set根据freq存数据,但这么做每次操作需要O(lgN)时间。如果不考虑频率,题目就退成了LRU cache,用double linked list配合hash map。现在引入频率,相当于对每个频率都维持了一个LRU cache,当然为了方便查找它们共用一个hash map。

答案参考了花花的。

/*
 * @lc app=leetcode id=460 lang=cpp
 *
 * [460] LFU Cache
 *
 * https://leetcode.com/problems/lfu-cache/description/
 *
 * algorithms
 * Hard (29.73%)
 * Likes:    842
 * Dislikes: 90
 * Total Accepted:    49.1K
 * Total Submissions: 161.3K
 * Testcase Example:  '["LFUCache","put","put","get","put","get","get","put","get","get","get"]\n[[2],[1,1],[2,2],[1],[3,3],[2],[3],[4,4],[1],[3],[4]]'
 *
 * 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.
 * 
 * Note that the number of times an item is used is the number of calls to the
 * get and put functions for that item since it was inserted. This number is
 * set to zero when the item is removed.
 * 
 * 
 * 
 * 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
 * 
 * 
 * 
 * 
 */

// @lc code=start
class LFUCache {
public:
    class Node {
    public:
        int key;
        int val;
        int freq;
        list<int>::const_iterator it;
        Node(int k, int v, int f, list<int>::const_iterator i): key(k), val(v), freq(f), it(i) {}
    };

    int cap, min_freq;
    unordered_map<int, Node*> m;
    unordered_map<int, list<int>> freqs;

    LFUCache(int capacity) {
        cap = capacity;
        min_freq = 0;
    }

    void touch(Node *node) {
        // update the freq
        const int prev_freq = node->freq;
        const int freq = ++(node->freq);
        // rm the node from old freq list
        freqs[prev_freq].erase(node->it);
        if (freqs[prev_freq].empty() && prev_freq == min_freq) {
            freqs.erase(prev_freq);
            ++min_freq;
        }
        // insert the key to the front of the new freq list
        freqs[freq].push_front(node->key);

        // update the pointer
        node->it = freqs[freq].cbegin();
    }
    
    int get(int key) {
        auto it = m.find(key);
        if (it == m.end()) return -1;
        touch(it->second);
        return it->second->val;
    }
    
    void put(int key, int value) {
        if (cap == 0) return;
        auto it = m.find(key);
        if (it != m.cend()) {
            it->second->val = value;
            touch(it->second); 
            return;
        }
        if (m.size() == cap) {
            // full, need to remove
            // rm from the min_freq list
            const int key_to_del = freqs[min_freq].back();
            freqs[min_freq].pop_back();
            m.erase(key_to_del);
        }
        min_freq = 1;
        freqs[min_freq].push_front(key);
        m[key] = new Node(key, value, min_freq, freqs[min_freq].cbegin());
    }
};

/**
 * 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);
 */
// @lc code=end

Last updated