数据结构算法---LRF缓存

  1. 对于LFU算法,需要在O(1)的时间复杂度内满足以下需求:

    1. 调用get(key)方法时,返回该key对应的val

    2. 只要用get或者put方法访问一次某个key,该key的freq就要加一

    3. 如果在插入元素时容量已满,则需要将freq最小的key删除;如果最小的freq对应多个key,则删除其中最旧的那个

    对应于上述这些需求,分析需求来确定应该使用什么样的数据结构:

    1. 使用一个HashMap来存储key到val的映射,就可以快速得到get(key)

    2. 使用一个HashMap来存储key到freq的映射,就可以快速得到key所对应的freq

    3. a. 需要从freq到key的映射,且要在O(1)的时间内将当前freq最小的key删除,则用一个变量minFreq来记录当前最小的freq.

      b. freq为最小的key值可能有多个,因此一个freq存储应该对应一个key的集合

      c. 在b中集合中的key应该是有序的,以此来区分最久未使用或者说是最旧的那个key。因此这里的集合应该采用链表结构来保证顺序

      综上分析,应该使用HashMap>来存储freq到key值的映射关系

  2. 具体的实现逻辑如下:

import java.util.HashMap;
import java.util.LinkedHashSet;

/**
 * @Desc   Leetcood 460 :LFU缓存
 * @Author 嗜雪的蚂蚁
 * @Date 2022/1/13 11:18
 **/
public class LFUCache {
    //存储键值对映射
    HashMap kv;
    //存储键和其频率映射
    HashMap kf;
    //存储某一频率的键列表
    HashMap> fkset;
    //当前的最小频率
    int minFreq;
    //LFU缓存的最大容量,用于判断是否需清理
    int capacity;

    public LFUCache(int capacity) {
        this.kv = new HashMap<>();
        this.kf = new HashMap<>();
        this.fkset = new HashMap<>();
        this.minFreq = 1;
        this.capacity = capacity;
    }

    //获取操作
    public int get(int key) {
        if (!kv.containsKey(key)){
            return -1;
        }
        increaseFreq(key);
        return kv.get(key);
    }

    //插入操作
    public void put(int key, int value) {
        if (capacity<=0) return;
        //当前插入的key已存在时
        if (kv.containsKey(key)){
            //插入数据
            kv.put(key,value);
            increaseFreq(key);
            return;
        }
        //容量超出
        if (capacity<=kv.size()){
            removeLeastFreq();
        }
        //处理新插入数据及其相关的三个哈希表映射
        kv.put(key,value);
        kf.put(key,1);
        fkset.putIfAbsent(1, new LinkedHashSet<>());
        fkset.get(1).add(key);
        this.minFreq=1;
    }

    //清理掉最近最不常用数据(使用频率最小)
    private void removeLeastFreq() {
        //LinkedHashSet最新插入的元素应该在链表尾部,因此获取的第一个元素即为最早插入的元素
        LinkedHashSet keySet = fkset.get(this.minFreq);
        Integer deleteKey = keySet.iterator().next();
        keySet.remove(deleteKey);
        if (keySet.isEmpty()){
            fkset.remove(this.minFreq);
        }
        //更新kf表
        kf.remove(deleteKey);
        //更新kv表
        kv.remove(deleteKey);
    }

    //增加传入key的频率,需要同时更新kf和fkset两个表
    private void increaseFreq(int key) {
        //更新kf表
        Integer freq = kf.get(key);
        kf.put(key,freq+1);
        //更新fkset表
        fkset.get(freq).remove(key);
        fkset.putIfAbsent(freq+1,new LinkedHashSet<>());
        fkset.get(freq+1).add(key);
        if (fkset.get(freq).isEmpty()){
            fkset.remove(freq);
            //若当前移除的恰好是最小频率集合,则需更新最小频率
            if (freq == minFreq){
                this.minFreq++;
            }
        }
    }
}

你可能感兴趣的:(数据结构算法---LRF缓存)