zoukankan      html  css  js  c++  java
  • [LeetCode每日1题][困难] 460. LFU缓存

    题目

    460. LFU缓存 - 力扣(LeetCode)
    在这里插入图片描述

    哈希表 + 平衡二叉树 - O(logN)

    一个平衡二叉树用于对结点的频率时间从大到小排序,这里时间用一个变量time维护即可,每次使用时++time。一个哈希表用于查找结点。

    
    struct Node {
        int cnt, time, key, value;
    
        Node(int _cnt, int _time, int _key, int _value):cnt(_cnt), time(_time), key(_key), value(_value){}
        
        bool operator < (const Node& rhs) const {
            return cnt == rhs.cnt ? time < rhs.time : cnt < rhs.cnt;
        }
    };
    class LFUCache {
        // 缓存容量,时间戳
        int capacity, time;
        unordered_map<int, Node> key_table;
        set<Node> S;
    public:
        LFUCache(int _capacity) {
            capacity = _capacity;
            time = 0;
            key_table.clear();
            S.clear();
        }
        
        int get(int key) {
            if (capacity == 0) return -1;
            auto it = key_table.find(key);
            // 如果哈希表中没有键 key,返回 -1
            if (it == key_table.end()) return -1;
            // 从哈希表中得到旧的缓存
            Node cache = it -> second;
            // 从平衡二叉树中删除旧的缓存
            S.erase(cache);
            // 将旧缓存更新
            cache.cnt += 1;
            cache.time = ++time;
            // 将新缓存重新放入哈希表和平衡二叉树中
            S.insert(cache);
            it -> second = cache;
            return cache.value;
        }
        
        void put(int key, int value) {
            if (capacity == 0) return;
            auto it = key_table.find(key);
            if (it == key_table.end()) {
                // 如果到达缓存容量上限
                if (key_table.size() == capacity) {
                    // 从哈希表和平衡二叉树中删除最近最少使用的缓存
                    key_table.erase(S.begin() -> key);
                    S.erase(S.begin());
                }
                // 创建新的缓存
                Node cache = Node(1, ++time, key, value);
                // 将新缓存放入哈希表和平衡二叉树中
                key_table.insert(make_pair(key, cache));
                S.insert(cache);
            }
            else {
                // 这里和 get() 函数类似
                Node cache = it -> second;
                S.erase(cache);
                cache.cnt += 1;
                cache.time = ++time;
                cache.value = value;
                S.insert(cache);
                it -> second = cache;
            }
        }
    };
    
    作者:LeetCode-Solution
    链接:https://leetcode-cn.com/problems/lfu-cache/solution/lfuhuan-cun-by-leetcode-solution/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    

    双哈希表 - O(1)

    简单地说就是,一个map<int,list<Node>::iterator>,用来存key对应的Node的地址,一个map<int,list<Node>>,用来存某一frequency对应的所有Node的链表。插入的时候,根据frequency,将结点插入到对应链表的头部,显然越靠头部的结点,越被最近使用。移除的时候,移除最小frequancy对应结点的尾部即可。还有一些小细节需要处理,如更新minfreq和移除空链表。还是直接看官方的吧……
    在这里插入图片描述

    // 缓存的节点信息
    struct Node {
        int key, val, freq;
        Node(int _key,int _val,int _freq): key(_key), val(_val), freq(_freq){}
    };
    class LFUCache {
        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;
            auto 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);
            // 如果当前链表为空,我们需要在哈希表中删除,且更新minFreq
            if (freq_table[freq].size() == 0) {
                freq_table.erase(freq);
                if (minfreq == freq) minfreq += 1;
            }
            // 插入到 freq + 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;
            auto it = key_table.find(key);
            if (it == key_table.end()) {
                // 缓存已满,需要进行删除操作
                if (key_table.size() == capacity) {
                    // 通过 minFreq 拿到 freq_table[minFreq] 链表的末尾节点
                    auto it2 = freq_table[minfreq].back();
                    key_table.erase(it2.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 {
                // 与 get 操作基本一致,除了需要更新缓存的值
                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[key] = freq_table[freq + 1].begin();
            }
        }
    };
    
    作者:LeetCode-Solution
    链接:https://leetcode-cn.com/problems/lfu-cache/solution/lfuhuan-cun-by-leetcode-solution/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    
    

    参考

    LFU缓存 - LFU缓存 - 力扣(LeetCode)

  • 相关阅读:
    rman备份,恢复
    异步事件回调机制原理探索 (转)
    stock
    将知识变成你的技能点
    Tomcat的URL中文乱码解决以及传输优化
    李洪强iOS开发之-入门指南
    WebSocket 和 Socket 的区别
    李洪强iOS开发之-修改状态栏的字体的颜色
    关于UDID和UUID的区别
    李洪强iOS开发之
  • 原文地址:https://www.cnblogs.com/zaynq/p/12679066.html
Copyright © 2011-2022 走看看