LeetCode之LFU缓存机制

LeetCode之LFU缓存机制

  • 请你为 最不经常使用(LFU)缓存算法设计并实现数据结构。
  • 实现 LFUCache 类:
class LFUCache {
	
	//用数据结构的容量 capacity 初始化对象
    public LFUCache(int capacity) {
        
    }
	//如果键存在于缓存中,则获取键的值,否则返回 -1。
    public int get(int key) {
    
    }
	// 如果键已存在,则变更其值;如果键不存在,请插入键值对。
	// 当缓存达到其容量时,则应该在插入新项之前,删除 最不经常使用的项。
	// 在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,应该去除 最久未使用 的键。
	// 注意「项的使用次数」就是自插入该项以来对其调用 get 和 put 函数的次数之和。使用次数会在对应项被移除后置为 0 。
	// 为了确定最不常使用的键,可以为缓存中的每个键维护一个 使用计数器。
	//当一个键首次插入到缓存中时,它的使用计数器被设置为 1 (由于 put 操作)。对缓存中的键执行 get 或 put 操作,使用计数器的值将会递增。
    public void put(int key, int value) {
 		
    }
}

/**
 * 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);
 */
  • 需求:

    1. 调用get方法,可以根据 键key 快速 获取value,调用put方法,可以将 键值对 存储起来
    2. 之后每次调用get或put方法,键值对的 使用次数 应该+1
    3. 调用put方法时,如果发现容量已经达到了上限,那么首先会删除那个 使用次数最少 的项;如果有多个 使用次数最少 的项,那么删除其中 最久未使用 的项。
  • 分析:

    1. 为了满足需求1,使用HashMap<Integer, Integer> key2Val来保存键值对
    2. 为了满足需求2,使用HashMap<Integer, Integer> key2Freq来保存键及其使用次数
    3. 为了满足需求3中,快速找到 最少的使用次数,设置int minFreq 来保存 最少的使用次数
    4. 为了能够快速找到使用次数最少的项,使用HashMap<Integer, key的列表> freq2Keys来保存 使用次数到键 的映射,另外为了能够在 多个使用次数最少的项中 删除最久未使用的项,那么希望key的列表是有序的。所以,或许可以使用LinkedList来保存 key的列表。但是我们还有一个需求,在使用了某个key之后,需要快速在 key的列表 中找到这个key,并从 这个key的列表 中删除,加入使用次数+1的 key的列表,所以,我们需要使用LinkedHashSet来保存key的列表。
    5. LinkedHashSet 是链表和哈希集合的结合体。链表不能快速访问链表节点,但是插入元素具有时序;哈希集合中的元素无序,但是可以对元素进行快速的访问和删除。那么,它俩结合起来就兼具了哈希集合和链表的特性,既可以在 O(1) 时间内访问或删除其中的元素,又可以保持插入的时序。
  • 代码实现

class LFUCache {
    private HashMap<Integer, Integer> key2Val;
    private HashMap<Integer, Integer> key2Freq;
    private HashMap<Integer, LinkedHashSet<Integer>> freq2Keys;
    private int capacity;
    private int minFreq;

    public LFUCache(int capacity) {
        this.key2Val = new HashMap<>();
        this.key2Freq = new HashMap<>();
        this.freq2Keys = new HashMap<>();
        this.capacity = capacity;
        this.minFreq = 1;
    }

    public int get(int key) {
        boolean containsKey = key2Val.containsKey(key);
        if(containsKey){
            int val = key2Val.get(key);
            //增加key的使用次数
            increaseFreq(key);
            return val;
        }
        else{
            return -1;
        }
    }

    public void put(int key, int value) {
        if(this.capacity<=0){
            return;
        }
        boolean containsKey = key2Val.containsKey(key);
        //存在key
        if(containsKey){
            //修改键值对
            key2Val.put(key,value);
            //增加key的使用次数
            increaseFreq(key);
        }
        else {
        	//达到了 容量上限
            if(key2Val.size()==capacity){
                //删除最少使用的键值对
                deleteMinFreq();
            }
            //加入新节点
            //更新kv
            key2Val.put(key,value);
            //更新kf
            key2Freq.put(key,1);
            //更新fk
            LinkedHashSet<Integer> orDefault = freq2Keys.getOrDefault(1, null);
            if(orDefault==null){
                LinkedHashSet<Integer> set = new LinkedHashSet<>();
                set.add(key);
                freq2Keys.put(1,set);
            }
            else {
                orDefault.add(key);
            }
            //更新minFreq
            minFreq = 1;
        }
    }

    private void deleteMinFreq(){
        //更新fk
        LinkedHashSet<Integer> keys = freq2Keys.get(this.minFreq);
        int deleteKey = keys.iterator().next();//linkedHashSet中第一个元素就是最先加入的元素
        keys.remove(deleteKey);
        if(keys.isEmpty()){
            freq2Keys.remove(this.minFreq);
        }
        //更新kv
        key2Val.remove(deleteKey);
        //更新kf
        key2Freq.remove(deleteKey);
    }

    private void increaseFreq(int key){
        int freq = key2Freq.get(key);
        //更新kf
        key2Freq.put(key,freq+1);
        //更新fk
        LinkedHashSet<Integer> keys = freq2Keys.get(freq);
        keys.remove(key);
        if (keys.isEmpty()){
            freq2Keys.remove(freq);
            //更新minKey
            if(this.minFreq == freq){
                this.minFreq++;
            }
        }
        LinkedHashSet<Integer> orDefault = freq2Keys.getOrDefault(freq + 1, null);
        if(orDefault==null){
            LinkedHashSet<Integer> set = new LinkedHashSet<>();
            set.add(key);
            freq2Keys.put(freq+1,set);
        }
        else {
            orDefault.add(key);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值