LFU

作者: Oliver_Li | 来源:发表于2021-01-31 21:20 被阅读0次
    • LFU:least frequently used最不经常使用
    • LFU比LRU难一点,加入了使用量的概念,需要维护使用量的计数器。
    • 题目:
      当缓存达到其容量时,剔除使用次数最少的节点。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,应该去除 最久未使用的键。

    思路:

    • 没想通,结果直接看了答案...
    • 开始的想法:kv存储,肯定需要map,但是和LRU不同,想着维护一个数组,下标代表使用次数,嵌套List,put时用头插法,每次清除最小下标List的尾节点即可,但如果访问量大数组会空余出很多节点。
    • 看了答案(官方):
      我们定义两个哈希表,第一个 freq_table 以频率 freq 为索引,每个索引存放一个双向链表,这个链表里存放所有使用频率为 freq 的缓存,缓存里存放三个信息,分别为键 key,值 value,以及使用频率 freq。第二个 key_table 以键值 key 为索引,每个索引存放对应缓存在 freq_table 中链表里的内存地址,这样我们就能利用两个哈希表来使得两个操作的时间复杂度均为 O(1)O(1)。同时需要记录一个当前缓存最少使用的频率 minFreq,这是为了删除操作服务的。
    • 其他就是维护了一个最小使用的key,key的尾节点就是待删除节点。
    // 官方代码,直接贴了...
    class LFUCache {
        int minfreq, capacity;
        Map<Integer, Node> key_table;
        Map<Integer, LinkedList<Node>> freq_table;
    
        public LFUCache(int capacity) {
            this.minfreq = 0;
            this.capacity = capacity;
            key_table = new HashMap<Integer, Node>();;
            freq_table = new HashMap<Integer, LinkedList<Node>>();
        }
        
        public int get(int key) {
            if (capacity == 0) {
                return -1;
            }
            if (!key_table.containsKey(key)) {
                return -1;
            }
            Node node = key_table.get(key);
            int val = node.val, freq = node.freq;
            freq_table.get(freq).remove(node);
            // 如果当前链表为空,我们需要在哈希表中删除,且更新minFreq
            if (freq_table.get(freq).size() == 0) {
                freq_table.remove(freq);
                if (minfreq == freq) {
                    minfreq += 1;
                }
            }
            // 插入到 freq + 1 中
            LinkedList<Node> list = freq_table.getOrDefault(freq + 1, new LinkedList<Node>());
            list.offerFirst(new Node(key, val, freq + 1));
            freq_table.put(freq + 1, list);
            key_table.put(key, freq_table.get(freq + 1).peekFirst());
            return val;
        }
        
        public void put(int key, int value) {
            if (capacity == 0) {
                return;
            }
            if (!key_table.containsKey(key)) {
                // 缓存已满,需要进行删除操作
                if (key_table.size() == capacity) {
                    // 通过 minFreq 拿到 freq_table[minFreq] 链表的末尾节点
                    Node node = freq_table.get(minfreq).peekLast();
                    key_table.remove(node.key);
                    freq_table.get(minfreq).pollLast();
                    if (freq_table.get(minfreq).size() == 0) {
                        freq_table.remove(minfreq);
                    }
                }
                LinkedList<Node> list = freq_table.getOrDefault(1, new LinkedList<Node>());
                list.offerFirst(new Node(key, value, 1));
                freq_table.put(1, list);
                key_table.put(key, freq_table.get(1).peekFirst());
                minfreq = 1;
            } else {
                // 与 get 操作基本一致,除了需要更新缓存的值
                Node node = key_table.get(key);
                int freq = node.freq;
                freq_table.get(freq).remove(node);
                if (freq_table.get(freq).size() == 0) {
                    freq_table.remove(freq);
                    if (minfreq == freq) {
                        minfreq += 1;
                    }
                }
                LinkedList<Node> list = freq_table.getOrDefault(freq + 1, new LinkedList<Node>());
                list.offerFirst(new Node(key, value, freq + 1));
                freq_table.put(freq + 1, list);
                key_table.put(key, freq_table.get(freq + 1).peekFirst());
            }
        }
    }
    
    class Node {
        int key, val, freq;
    
        Node(int key, int val, int freq) {
            this.key = key;
            this.val = val;
            this.freq = freq;
        }
    }
    

    相关文章

      网友评论

        本文标题:LFU

        本文链接:https://www.haomeiwen.com/subject/qsfdtltx.html