LFU缓存

class LFUCache {
    /**
     * 基本原理:使用LinkedHashSet实现 元素有序+快速增删查改。LinkedHashSet底层使用 LinkedHashMap。
     *
     * 写代码步骤:
     * 1、搭大框架。建立 KV KF FV 映射,minFeq变量记录最小freq值
     * 2、实现2个供get/put方法调用的API:increaseFreq、removeMinFreqKey
     * 3、实现get/put方法
     * @param capacity
     */

    HashMap<Integer,Integer> keyToValue; //key到value的映射,称为KV表
    HashMap<Integer,Integer> keyToFreq; //key到freq(使用频率)的映射,称为KF表
    // 注意,因为可能有多个key使用频率相同,所以一个freq对应多个key。用LinkedHashSet来装多个key,LinkedHashSet内部对key按最近最少使用排序(LRU),左边(靠近头部)是最近最少使用的,右边(靠近尾部)是最近使用过的。
    HashMap<Integer, LinkedHashSet<Integer>> freqToKeys; //freq到多个key的映射,称为FK表。
    int minFreq; //记录最小频率值
    int cap; //最大容量

    public LFUCache(int capacity) {
        keyToValue = new HashMap<>();
        keyToFreq = new HashMap<>();
        freqToKeys = new HashMap<>();
        minFreq = 0;
        cap = capacity;
    }

    public int get(int key) {
        if(!keyToValue.containsKey(key)){
            return -1;
        }
        increaseFreq(key);
        return keyToValue.get(key);
    }

    public void put(int key, int value) {
        if(cap<=0){
            return;
        }
        
        if(keyToValue.containsKey(key)){ //已经存在相同key
            keyToValue.put(key,value);
            increaseFreq(key);
            return;
        }

        if(keyToValue.size() == cap){ //容量满了,移除使用频次最少的key
            removeMinFreqKey();
        }

        //加入新元素

        //插入KV表
        keyToValue.put(key,value);
        //插入KF表
        keyToFreq.put(key,1);
        //插入FK表
        freqToKeys.putIfAbsent(1,new LinkedHashSet<>());
        freqToKeys.get(1).add(key);
        //插入新key后minFreq肯定是1
        minFreq = 1;

    }


    //实现2个供get/put方法调用的API:increaseFreq、removeMinFreqKey
    //核心就是对3个映射表操作,不要漏掉

    public void increaseFreq(int key){
        //更新KF表
        int freq = keyToFreq.get(key);
        keyToFreq.put(key,freq+1);

        //更新FK表
        freqToKeys.get(freq).remove(key); //从原来的freq删除
        freqToKeys.putIfAbsent(freq+1, new LinkedHashSet<>()); //先判断是否有新的freq阵营。putIfAbsent:如果已经存在,则不做改变;如果不存在,则加入新元素
        freqToKeys.get(freq+1).add(key); //将该key加入到新的freq阵营

        //如果老的freq阵营因为移除key而变空了,就把那个阵营删掉
        if(freqToKeys.get(freq).isEmpty()){
            freqToKeys.remove(freq);
            //如果这个freq恰好是minFreq,更新minFreq
            if(freq == minFreq){
                minFreq ++; //新的minFreq肯定是原来+1,因为刚刚使key加入新阵营freq+1了
            }
        }
    }

    public void removeMinFreqKey(){
        //在FK表找到要删除的key,并删除之
        LinkedHashSet<Integer> keysList = freqToKeys.get(minFreq); //获取最小频率对应的key表
        int deleteKey = keysList.iterator().next(); //排在LinkedHashSet首位的就是最近最久未使用的key,要被删除
        keysList.remove(deleteKey); //删除deleteKey
        if(keysList.isEmpty()){
            freqToKeys.remove(minFreq); //如果minFreq对应的key都没了,则删除这个阵营
            //这里不用给minFreq赋新值,因为是put调用的removeMinFreqKey方法,后面会put新元素,put新元素时minFreq=1
        }

        //在KV表删除
        keyToValue.remove(deleteKey);
        //在KF表删除
        keyToFreq.remove(deleteKey);



    }


}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qtayu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值