手写LFU算法

手写LFU算法(Least Frequently Used)

分析:

LFU算法相当于把数据按照访问频次进行排序,而且还有一种情况,如果多个数据拥有相同的访问词频,就应该删除最早插入的那个数据,也就是LFU算法是淘汰访问频次最低的数据,如果访问频次最低的数据有多条,需要淘汰最旧的数据

思路:

  1. 先从简单的开始分析,列举算法执行过程中的几个显而易见的事实
    1. 调用get(key)方法时,需要返回对应的val
    2. 只要用get或者put方法访问一次某个key,该key的freq就要加一
    3. 如果在容量满的时候进行插入,则需要将freq最小的key删除,如果是最小的freq对应多个key,则删除其中最旧的那个

一定要搞清楚映射关系,如果我们更新某个key对应的freq,那么就需要同步修改kf 和 fk表,这样才不会出问题

  1. 增加应该算是LFU算法的核心了,添加 val 和key 值,如果key已经存在,修改key对应的val 增加对应的freq 如果key不存在,需要新插入key,判断容量是否满了,满了就需要淘汰掉最小的freq,如果容量为满,插入key和val key对应的freq为1
  2. 删除某个键key肯定是需要同时修改三个映射表的,借助minFreq参数可以从FK表中找到freq最小的keyLIst。然后根据时序进行获取就可以了。
  3. 理清楚增加的操作,根据ke值获取到他对应的频率,然后再keyToVal里面进行重新的复制,这里会利用到map的特性就是key是可以相同的,然后将freqtokeys中的对应频率的表LinkedHashSet表进行修改,freq+1,如果freqToKey中有着个值对应的数据,我们只需要把当前的键继续添加进去就好了,但是还没有结束,如果freq对应的列表空了,我们就需要在表中移除这个对应的freq的键,如果这个freq == minFreq 则最小的次频++
public class LFUCache {
    //key到val的映射,
    HashMap<Integer, Integer> keyToVal;
    //key到Freq的映射
    HashMap<Integer, Integer> keyToFreq;
    //Freq到key的映射,方便我们查询到次频相同数字是进行改动
    HashMap<Integer, LinkedHashSet<Integer>> freqToKey;
    int minFreq;
    int cap;

    public LFUCache(int capacity) {
        keyToVal = new HashMap<>();
        keyToFreq = new HashMap<>();
        freqToKey = new HashMap<>();
        this.cap = capacity;
        this.minFreq = 0;
    }

    public int get(int key) {
        if (!keyToFreq.containsKey(key)) {
            return -1;
        }
        //增加key对应的freq
        increaseFreq(key);
        return keyToFreq.get(key);
    }

    public void put(int key, int val) {
        if (this.cap <= 0) {
            return;
        }
        /*若对应的key已经存在,修改对应的val即可*/
        if (keyToVal.containsKey(key)) {
            keyToVal.put(key,val);
            increaseFreq(key);
            return;
        }
       /*key不存在,则需要插入*/
        if (this.cap <= keyToVal.size()) {
            removeMinFreqKey();
        }
        /*插入对应的key和val 对应的freq为1*/
        keyToVal.put(key,val);
        keyToFreq.put(key,1);
        freqToKey.putIfAbsent(1,new LinkedHashSet<>());
        freqToKey.get(1).add(key);
        //插入新key最小的freq肯定是1
        this.minFreq = 1;
    }

    private void increaseFreq(int key) {
        int freq = keyToFreq.get(key);
        keyToFreq.put(key,freq + 1);
        freqToKey.get(freq).remove(key);
        freqToKey.putIfAbsent(freq + 1,new LinkedHashSet<>());
        freqToKey.get(freq + 1).add(key);
        //如果freq对应的列表空了,就移除这个freq
        if (freqToKey.get(freq).isEmpty()) {
            freqToKey.remove(freq);
            //如果这个freq恰好是minFreq,就可以更新minFreq
            if (freq == this.minFreq){
                this.minFreq++;
            }
        }
    }

    private void removeMinFreqKey() {
        LinkedHashSet<Integer> keyList = freqToKey.get(this.minFreq);
        int delteKey = keyList.iterator().next();
        //进行一系列数据的更新
        keyList.remove(delteKey);
        if (keyList.isEmpty()) {
            freqToKey.remove(this.minFreq);
            //这里的min需要进行更新么?
        }
        keyToVal.remove(delteKey);
        keyToFreq.remove(delteKey);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值