LRU题目姊妹篇,个人认为这道题难度比LRU大,主要需要记录访问次数,如果访问次数一致再按照LRU访问时间。解题思路参考了力扣官方文章
C++里可以使用set(底层红黑树),排序依据是访问次数和访问时间,因此需要重载比较运算符。
方法一AC代码:

struct Node {
    int m_cnt, m_time, m_key, m_value;
    Node(int cnt, int time, int key, int value):m_cnt(cnt), m_time(time), m_key(key), m_value(value){}
    
    bool operator < (const Node& rhs) const {
        return m_cnt == rhs.m_cnt ? m_time < rhs.m_time : m_cnt < rhs.m_cnt;
    }
};

class LFUCache {
private:
    int m_capacity, m_time;
    unordered_map<int, Node> m_cache;
    set<Node> m_set;

public:
    LFUCache(int capacity):m_capacity(capacity),m_time(0) {}
    
    int get(int key) {
        auto it = m_cache.find(key);
        if(it == m_cache.end())
            return -1;
        Node node = it->second;
        m_set.erase(node);
        node.m_cnt++;
        node.m_time = ++m_time;
        
        m_set.insert(node);
        it->second = node;
        return node.m_value;
    }
    
    void put(int key, int value) {
        if(m_capacity == 0) return;
        auto it = m_cache.find(key);
        if(it == m_cache.end())
        {
            if(m_cache.size() == m_capacity)
            {
                m_cache.erase(m_set.begin()->m_key);
                m_set.erase(m_set.begin());
            }
            
            Node node = Node(1,++m_time,key,value);
            m_set.insert(node);
            m_cache.insert(make_pair(key,node));
        }
        else
        {
            Node node = it->second;
            m_set.erase(node);
            
            node.m_time = m_time++;
            node.m_cnt++;
            node.m_value = value;
            
            m_set.insert(node);
            
            it->second = node;
        }
        
    }
};

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

实现的时候遇到问题,调试的代码(代码中存在bug)

#include <iostream>
#include <set>
#include <map>
#include <unordered_map>

using namespace std;

struct Node {
    int m_cnt, m_time, m_key, m_value;
    Node(int cnt, int time, int key, int value):m_cnt(cnt), m_time(time), m_key(key), m_value(value){}

    bool operator < (const Node& rhs) const {
        return m_cnt == rhs.m_cnt ? m_time < rhs.m_time : m_cnt < rhs.m_cnt;
    }
};

class LFUCache {
private:
    int m_capacity, m_time;
    unordered_map<int, Node> m_cache;
    set<Node> m_set;

public:
    LFUCache(int capacity):m_capacity(capacity),m_time(0) {
        m_cache.clear();
        m_set.clear();
    }

    int get(int key) {
        auto it = m_cache.find(key);
        if(it == m_cache.end())
            return -1;
        Node node = it->second;
        m_set.erase(node);
        node.m_cnt++;
        node.m_time++;

        m_set.insert(node);
        it->second = node;
        return node.m_value;
    }

    void put(int key, int value) {
        if(m_capacity == 0) return;
        auto it = m_cache.find(key);
        if(it == m_cache.end())
        {
            if(m_cache.size() == m_capacity)
            {
                m_cache.erase(m_set.begin()->m_key);
                m_set.erase(m_set.begin());
            }

            Node node = Node(1,++m_time,key,value);
            m_set.insert(node);
            m_cache.insert(make_pair(key,node));
        }
        else
        {
            Node node = it->second;
            m_set.erase(it->second);

            node.m_time++;
            node.m_cnt++;
            node.m_value = value;

            m_set.insert(node);

            it->second = node;
        }

    }
};

int main()
{
    LFUCache cache(2);
    cache.put(2,1);
    cache.put(3,2);
    cache.get(3);
    cache.get(2);
    cache.put(4,3);
    int ans = cache.get(2);
    cout << ans << endl;
    cache.get(3);
    cache.get(4);
    return 0;
}

方法二:双哈希表
双哈希表比较巧妙,一个哈希表维护key和迭代器,另一个哈希表维护相同频率的链表。

struct Node {
    int key, val, freq;
    Node(int _key,int _val,int _freq): key(_key), val(_val), freq(_freq){}
};

class LFUCache {
private:
    int minfreq, capacity;
    unordered_map<int, list<Node>::iterator> key_table;
    unordered_map<int, list<Node>> freq_table;
public:
    LFUCache(int _capacity) :minfreq(0),capacity(_capacity){
        key_table.clear();
        freq_table.clear();
    }
    
    int get(int key) {
        if(capacity == 0) return -1;
        unordered_map<int, list<Node>::iterator>::iterator it = key_table.find(key);
        if(it == key_table.end()) return -1;
        list<Node>::iterator node = it->second;
        
        int val = node->val,freq = node->freq;
        
        freq_table[freq].erase(node);
        //如果链表为空,需要删除key_table
        if(freq_table[freq].size() == 0){
            freq_table.erase(freq);
            
            if(minfreq == freq) minfreq += 1;
        }
        
        freq_table[freq + 1].push_front(Node(key,val,freq + 1));
        key_table[key] = freq_table[freq + 1].begin();
        
        return val;
    }
    
    void put(int key, int value) {
        if(capacity == 0) return;
        unordered_map<int, list<Node>::iterator>::iterator it = key_table.find(key);
        if(it == key_table.end()){
            if(key_table.size() == capacity){
                Node node = freq_table[minfreq].back();
                key_table.erase(node.key);
                freq_table[minfreq].pop_back();
                if(freq_table[minfreq].size() == 0)
                    freq_table.erase(minfreq);
            }
            
            freq_table[1].push_front(Node(key,value,1));
            key_table[key] = freq_table[1].begin();
            minfreq = 1;
        }else{
            list<Node>::iterator node = it->second;
            int freq = node->freq;

            freq_table[freq].erase(node);
            if(freq_table[freq].size() == 0){
                freq_table.erase(freq);
                if(minfreq == freq) minfreq += 1;
            }
            
            freq_table[freq + 1].push_front(Node(key,value,freq + 1));
            //key_table.insert(make_pair(key, freq_table[freq + 1].begin()));
            key_table[key] = freq_table[freq + 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);
 */

上面有一行注释代码,如果使用insert这里会报错,原因是已经有key键,插入失败,而我们又删除了对应freq_table中的元素,因而访问迭代器失效,通过Address Sanitizer 检测会报错如下:

=================================================================
==30==ERROR: AddressSanitizer: heap-use-after-free on address 0x6030000002f4 at pc 0x00000034e9a1 bp 0x7fffb0e87070 sp 0x7fffb0e87068

运行效率惊人100%

Runtime: 92 ms, faster than 100.00% of C++ online submissions for LFU Cache.
Memory Usage: 42.3 MB, less than 79.62% of C++ online submissions for LFU Cache.
Next challenges: