LeetCode 460. LFU Cache

Hashtable + BST

用hashtable保存key->Node,另外用一个set<Node>来根据freq和time进行排序,保证容量满时删除的是LFU的节点。

注意这里hashtable的value存的是Node,不是指针也不是set的iterator。因为用指针只能做到从set里删除O(1),而set的插入还是O(logn)的,总的时间复杂度O(logn)。

struct Node{
    int key;
    int value;
    int freq;
    int time;
    bool operator<(const Node &node) const{
        if (freq!=node.freq) return freq<node.freq;
        return time<node.time; // least recently used first
    }
};

class LFUCache {
private:
    int cap;
    int timestamp;
    unordered_map<int,Node> m; // key -> node
    set<Node> s;
    
    void update(Node &node){
        s.erase(node); // log(n)
        ++node.freq;
        node.time = timestamp++;
        s.insert(node); // log(n)
    }
    
public:
    LFUCache(int capacity) {
        cap = capacity;
        timestamp = 0;
    }
    
    int get(int key) {
        if (!m.count(key)) return -1;
        int res=m[key].value;
        update(m[key]);
        return res;
        return 0;
    }
    
    void put(int key, int value) {
        if (cap==0) return;
        if (m.count(key)){
            m[key].value = value;
            update(m[key]);
            return;
        }
        if (m.size()==cap){ // need to evict
            Node node=*s.begin();
            m.erase(node.key);
            s.erase(node);
        }
        Node newNode{key,value,1,timestamp++};
        m[key] = newNode;
        s.insert(newNode);
    }
};

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

时间复杂度 O(logn)

 

Hashtable + Linked List

这里思路就和LRU类似,用一个hashtable存放list的iterator。由于LFU要考虑frequency,我们建立 freq 到 list of keys with this freq 的映射,同时维护min_freq,并保证每个list都是最近访问的放在前面。

这样的好处是,如果我们要更新一个节点的freq,我们可以直接通过iterator在对应freq的list里找到并删除,并插入++freq对应list的最前面。

struct Node{
    int key;
    int value;
    int freq;
    list<int>::iterator it;
};

class LFUCache {
private:
    int cap;
    int min_freq;
    unordered_map<int,Node> m; // key -> node
    unordered_map<int,list<int>> l; // freq -> keys with freq
    
    void update(Node &node){
        int prev_freq=node.freq;
        int freq=++(node.freq);
        l[prev_freq].erase(node.it);
        if (l[prev_freq].empty() && min_freq==prev_freq){ // update min_freq
            l.erase(prev_freq);
            ++min_freq;
        }
        l[freq].push_front(node.key);
        node.it = l[freq].begin();
    }
    
public:
    LFUCache(int capacity) {
        cap = capacity;
        min_freq = 0;
    }
    
    int get(int key) {
        if (!m.count(key)) return -1;
        int res=m[key].value;
        update(m[key]);
        return res;
    }
    
    void put(int key, int value) {
        if (cap==0) return;
        if (m.count(key)){
            m[key].value = value;
            update(m[key]);
            return;
        }
        if (m.size()==cap){ // need to evict
            m.erase(l[min_freq].back());
            l[min_freq].pop_back();
        }
        min_freq = 1;
        l[1].push_front(key);
        m[key] = {key,value,1,l[1].begin()};
    }
};

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

时间复杂度 O(1) 

posted @ 2019-11-02 11:59  約束の空  阅读(144)  评论(0编辑  收藏  举报