• 460. LFU 缓存


     

    请你为 最不经常使用(LFU)缓存算法设计并实现数据结构。

    实现 LFUCache 类:

    • LFUCache(int capacity) - 用数据结构的容量 capacity 初始化对象
    • int get(int key) - 如果键 key 存在于缓存中,则获取键的值,否则返回 -1 。
    • void put(int key, int value) - 如果键 key 已存在,则变更其值;如果键不存在,请插入键值对。当缓存达到其容量 capacity 时,则应该在插入新项之前,移除最不经常使用的项。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,应该去除 最近最久未使用 的键。

    为了确定最不常使用的键,可以为缓存中的每个键维护一个 使用计数器 。使用计数最小的键是最久未使用的键。

    当一个键首次插入到缓存中时,它的使用计数器被设置为 1 (由于 put 操作)。对缓存中的键执行 get 或 put 操作,使用计数器的值将会递增。

    函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

    示例:

    输入:
    ["LFUCache", "put", "put", "get", "put", "get", "get", "put", "get", "get", "get"]
    [[2], [1, 1], [2, 2], [1], [3, 3], [2], [3], [4, 4], [1], [3], [4]]
    输出:
    [null, null, null, 1, null, -1, 3, null, -1, 3, 4]
    
    解释:
    // cnt(x) = 键 x 的使用计数
    // cache=[] 将显示最后一次使用的顺序(最左边的元素是最近的)
    LFUCache lfu = new LFUCache(2);
    lfu.put(1, 1);   // cache=[1,_], cnt(1)=1
    lfu.put(2, 2);   // cache=[2,1], cnt(2)=1, cnt(1)=1
    lfu.get(1);      // 返回 1
                     // cache=[1,2], cnt(2)=1, cnt(1)=2
    lfu.put(3, 3);   // 去除键 2 ,因为 cnt(2)=1 ,使用计数最小
                     // cache=[3,1], cnt(3)=1, cnt(1)=2
    lfu.get(2);      // 返回 -1(未找到)
    lfu.get(3);      // 返回 3
                     // cache=[3,1], cnt(3)=2, cnt(1)=2
    lfu.put(4, 4);   // 去除键 1 ,1 和 3 的 cnt 相同,但 1 最久未使用
                     // cache=[4,3], cnt(4)=1, cnt(3)=2
    lfu.get(1);      // 返回 -1(未找到)
    lfu.get(3);      // 返回 3
                     // cache=[3,4], cnt(4)=1, cnt(3)=3
    lfu.get(4);      // 返回 4
                     // cache=[3,4], cnt(4)=2, cnt(3)=3

    提示:

    • 0 <= capacity <= 104
    • 0 <= key <= 105
    • 0 <= value <= 109
    • 最多调用 2 * 105 次 get 和 put 方法
     
     
    struct doubleLinkedNode{
        int key;
        int val;
        int frequency;
        doubleLinkedNode* pre;
        doubleLinkedNode* next;
        doubleLinkedNode(int _key, int _val) {
            key = _key;
            val = _val;
            frequency = 1;
        }
    };
    struct doubleLinkedList{
        doubleLinkedNode* head;
        doubleLinkedNode* tail;
        doubleLinkedList(){
            head = new doubleLinkedNode(-1, -1);
            tail = new doubleLinkedNode(-1, -1);
            head->next = tail;
            tail->pre = head;
        }
    };
    class LFUCache {
    public:
        unordered_map<int, doubleLinkedNode*> KTN;
        unordered_map<int, doubleLinkedList> FTL; // freq to Linklist
        int maxsize;
        int cursize;
        int minfrequency;
    
        LFUCache(int capacity) {
            maxsize = capacity;
            cursize = 0;
            minfrequency = 0; // 将最小freq的 节点删除,需要保存最小 freq
        }
        int get(int key) {
            if(KTN.find(key) == KTN.end()) {
                return -1;
            } else {
                doubleLinkedNode* targetNode = KTN[key];
                int res = targetNode->val;
                removeNode(targetNode);
                targetNode->frequency += 1;
                headInsert(targetNode);
                return res;
            }
        }
        void put(int key, int value) {
            if(maxsize == 0)
                return;
            if(KTN.find(key) != KTN.end()){
                doubleLinkedNode* targetNode = KTN[key];
                targetNode->val = value;
                removeNode(targetNode);
                targetNode->frequency += 1;
                headInsert(targetNode);
            }else{
                if(cursize == maxsize){
                    deleteNode(minfrequency);
                    cursize -= 1;
                }
                KTN[key]  = new doubleLinkedNode(key, value);
                minfrequency = 1;
                headInsert(KTN[key]);
                cursize += 1;
            }
        }
        void removeNode(doubleLinkedNode* node){
            node->pre->next = node->next;
            node->next->pre = node->pre;
            if(node->frequency == minfrequency && FTL[minfrequency].head->next == FTL[minfrequency].tail){
                // freq ==minfreq 的链表中只有1个元素。
                minfrequency += 1;
            }
        }
        void headInsert(doubleLinkedNode* node){
            node->pre = FTL[node->frequency].head;
            node->next = FTL[node->frequency].head->next;
            FTL[node->frequency].head->next = node;
            node->next->pre = node;
        }
        void deleteNode(int minfrequency){
            doubleLinkedNode* last = FTL[minfrequency].tail->pre;
            last->pre->next = last->next;
            last->next->pre = last->pre;
            KTN.erase(last->key);
        }
    };
  • 相关阅读:
    题解-AtCoder ARC-083F Collecting Balls
    题解-CTS2019氪金手游
    题解-CTS2019随机立方体
    题解-APIO2019路灯
    题解-APIO2019桥梁
    vue-property-decorator 源码阅读
    如何在Vue项目中使用TypeScript
    在 Vue+TypeScript 项目中,如何配置 ESLint 和 Prettier
    JavaScript 原型和原型链
    pre-commit + imagemin 实现图片自动压缩
  • 原文地址:https://www.cnblogs.com/zle1992/p/15765061.html
Copyright © 2020-2023  润新知