460、LFU缓存

  1. 460、LFU缓存
    1. 示例:
  2. 题解
    1. HashMap + TreeSet
    2. 双HashMap

460、LFU缓存

设计并实现最不经常使用(LFU)缓存的数据结构。它应该支持以下操作:get 和 put。

get(key) - 如果键存在于缓存中,则获取键的值(总是正数),否则返回 -1。
put(key, value) - 如果键不存在,请设置或插入值。当缓存达到其容量时,它应该在插入新项目之前,使最不经常使用的项目无效。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,最近最少使用的键将被去除。

进阶:

你是否可以在 O(1) 时间复杂度内执行两项操作?

示例:

LFUCache cache = new LFUCache( 2 /* capacity (缓存容量) */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // 返回 1
cache.put(3, 3);    // 去除 key 2
cache.get(2);       // 返回 -1 (未找到key 2)
cache.get(3);       // 返回 3
cache.put(4, 4);    // 去除 key 1
cache.get(1);       // 返回 -1 (未找到 key 1)
cache.get(3);       // 返回 3
cache.get(4);       // 返回 4

链接:https://leetcode-cn.com/problems/lfu-cache

题解

  • 解决get的时间复杂度为$O(1)$——Map
  • 解决put的时间复杂度为$O(1)$——有序集合,按照频率排序

HashMap + TreeSet

class LFUCache {
    // 节点类,具有大小比较的能力
    class Node implements Comparable<Node> {
        // 频率
        int cnt;
        // 上一次访问的时间
        int time;
        // key value
        int key;
        int value;
        public Node(int cnt,int time,int key,int value) {
            this.cnt = cnt;
            this.time = time;
            this.key = key;
            this.value = value;
        }
        // 频率比较器
        @Override 
        public int compareTo(Node n1) {
            return this.cnt == n1.cnt ? this.time - n1.time : this.cnt - n1.cnt;  
        }
    }

    int capacity,time;
    Map<Integer,Node> keyTable;
    TreeSet<Node> set;
    public LFUCache(int capacity) {
        this.capacity = capacity;
        keyTable = new HashMap<>(capacity);
        time = 0;
        set = new TreeSet<Node>();
    }
    
    public int get(int key) {
        // 0 容量
        if (capacity == 0) {return -1;}
        // key不存在
        if (!keyTable.containsKey(key)) {
            return -1;
        }
        // 更新节点状态
        Node cache = keyTable.get(key);
        set.remove(cache);
        cache.cnt += 1;
        cache.time = ++time;
        set.add(cache);
        keyTable.put(key,cache);
        // 返回目标的值
        return cache.value;
    }
    
    public void put(int key, int value) {
        // 0 容量
        if (capacity == 0) {return;}
        // 数据不存在
        if (!keyTable.containsKey(key)) {
            // 达到容量了,删除最久未访问的
            if (keyTable.size() == capacity) {
                Node node = set.first();
                System.out.println("node: "+node.key);
                keyTable.remove(node.key);
                set.remove(node);
            }
            // 插入新的值
            Node node = new Node(1,++time,key,value);
            keyTable.put(key,node);
            set.add(node);
        }else {
            // 更新节点状态
            Node cache = keyTable.get(key);
            set.remove(cache);
            cache.cnt += 1;
            cache.time = ++time;
            cache.value = value;
            keyTable.put(key,cache);
            set.add(cache);
        }
    }
}

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

双HashMap

  • key-node map
  • ferq-nodelist map
class LFUCache {
    class Node {
        int key,val;
        int freq;
        public Node(int key,int val,int freq) {
            this.key = key;
            this.val = val;
            this.freq = freq;
        }
    }
    int minfreq,capacity;
    Map<Integer,Node> keyTable;
    Map<Integer,LinkedList<Node>> freqTable;

    private void update(int key,int val,int freq) {
        Node newNode = new Node(key,val,freq);
        LinkedList<Node> nodeList = freqTable.get(freq) == null ? new LinkedList<>():freqTable.get(freq);
        nodeList.addFirst(newNode);
        freqTable.put(freq,nodeList);
        keyTable.put(key,newNode);
    }
    public LFUCache(int capacity) {
        minfreq = 0;
        this.capacity = capacity;
        keyTable = new HashMap<>();
        freqTable = new HashMap<>();
    }
    
    public int get(int key) {
        // 0 容量
        if (capacity == 0) {return -1;}
        // key不存在
        if(!keyTable.containsKey(key)) {
            return -1;
        }
        // 移除以便更新节点的频率
        Node node = keyTable.get(key);
        int val = node.val, freq = node.freq;
        freqTable.get(freq).remove(node);
        // 列表为空了
        if (freqTable.get(freq).isEmpty()) {
            // 移除
            freqTable.remove(freq);
            if (minfreq == freq) {minfreq += 1;}
        }
        update(key,val,freq+1);
        return node.val;
    }
    
    public void put(int key, int value) {
        if (capacity == 0) {return;}
        if (!keyTable.containsKey(key)){
            // 满了
            if (keyTable.size() == capacity) {
                Node node = freqTable.get(minfreq).getLast();
                keyTable.remove(node.key);
                freqTable.get(minfreq).removeLast();
                if (freqTable.get(minfreq).isEmpty()) {
                    freqTable.remove(minfreq);
                }
            }
            minfreq = 1;
            update(key,value,minfreq);
        } else {
            // 更新
            Node node = keyTable.get(key);
            int freq = node.freq;
            freqTable.get(freq).remove(node);
            if (freqTable.get(freq).isEmpty()) {
                freqTable.remove(freq);
                if (minfreq == freq) {minfreq += 1;}
                
            } 
            update(key,value,freq + 1);
        }
    }
}

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

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1056615746@qq.com

💰

Title:460、LFU缓存

Count:977

Author:攀登

Created At:2020-07-26, 00:19:44

Updated At:2024-06-15, 15:52:32

Url:http://jiafeimao-gjf.github.io/2020/07/26/460%E3%80%81LFU%E7%BC%93%E5%AD%98/

Copyright: 'Attribution-non-commercial-shared in the same way 4.0' Reprint please keep the original link and author.

×

Help us with donation