LFU缓存结构算法

设计LFU缓存结构

LFU:最近最少频率使用
基本思想: 当缓存满时,加入新数据,淘汰缓存中使用次数最少的key,当使用次数最少的key有多个,删除最早调用的key。

定义节点的数据结构

class Node{
		//使用频率
        int freq;
        //key值,用于数据更新的key
        int key;
        //value值
        int val;
        public Node(int freq, int key, int val){
            this.freq = freq;
            this.key = key;
            this.val = val;
        }
    }

确定实现缓存的的数据结构

  1. 定义一个<频率,链表>的频率集合freq_map
  2. 定义一个<key, node>的节点集合map
    链表存储的是相同使用频率的节点

freq_map的图示
当执行完[1,1,1],[1,2,2],[1,3,2]操作后,freq_map此时的状况:
频率为 1 的双向链表内有三个节点,按照插入顺序,节点每次都是从头部插入,删除节点每次都是尾部开始,目的是维护节点的调用秩序, 当频率相同时,删除最先使用的节点,即链表尾部的节点。
在这里插入图片描述

具体操作

  1. update
    1. 先获取节点的使用频率;
    2. 删除freq_map中双向链表中的对应节点
    3. 执行2之后,freq_map对应freq的链表可能为空,此时需要删除这个freq节点,注意如果该freq为最小频率,需要更新min_freq, 应为freq已经删除了,min_freq+1.
 private void update(Node node ,int key, int val){
    
    
        //找到频率
        int freq = node.freq;
        //原频率中删除该节点
        freq_map.get(freq).remove(node);
        //哈希表中该频率已经无节点,直接删除
        if(freq_map.get(freq).isEmpty()){
    
    
            freq_map.remove(freq);
            //若当前频率为最小,最小频率加1
            if(min_freq == freq){
    
    
                min_freq++;
            }
        }
        if(!freq_map.containsKey(freq+1)){
    
    
            freq_map.put(freq+1, new LinkedList<Node>());
        }
        //插入频率加1 的双向链表表头,链表中对应: freq key value
        freq_map.get(freq+1).addFirst(new Node(freq+1, key,val));
        map.put(key,freq_map.get(freq+1).getFirst());
    }
  1. set
    插入节点,首先判map中是否存在key:
    - 存在,调用update函数,进行数据更新操作
    - 不存在,插入新数据,但是插入之前需要判断缓存容量:
    1. 剩余空间为0, 即缓存已满,执行淘汰策略
    2. 剩余空间不为0,剩余空间-1,先存入ferq_map中,再存入map中。
 private void set(int key, int val){
    
    
        if(map.containsKey(key)){
    
    
            update(map.get(key),key,val);
        }else{
    
    
            if(size == 0){
    
    
                int oldkey = freq_map.get(min_freq).getLast().key;
                freq_map.get(min_freq).removeLast();
                if(freq_map.get(min_freq).isEmpty()){
    
    
                    freq_map.remove(min_freq);
                }
                //链表哈希表中删除
                map.remove(oldkey);
            }else{
    
    
                size--;
            }
            min_freq = 1;
            if(!freq_map.containsKey(1)){
    
    
                freq_map.put(1,new LinkedList<Node>());
            }
            freq_map.get(1).addFirst(new Node(1,key,val));
            map.put(key, freq_map.get(1).getFirst());
        }
    }
  1. get
    get操作在获取元素后,需要对freq_map和map进行更新操作。
private int get(int key){
    
    
        int res = -1;
        if(map.containsKey(key)){
    
    
            Node node =  map.get(key);
            res = node.val;
            update(node,key,res);
        }
        return res;
    }

完整代码

public class Solution {
    
    
    /**
     * lfu design
     * @param operators int整型二维数组 ops
     * @param k int整型 the k
     * @return int整型一维数组
     */
    class Node{
    
    
        int freq;
        int key;
        int val;
        public Node(int freq, int key, int val){
    
    
            this.freq = freq;
            this.key = key;
            this.val = val;
        }
    }
    //频率和双向链表的哈希表
    private Map<Integer, LinkedList<Node>> freq_map = new HashMap<>();
    
    //key到节点的哈希表
    private Map<Integer, Node> map = new HashMap<>();
    //记录缓存剩余容量
    private int size = 0;
    //记录当前最小频率
    private int min_freq = 0;

    public int[] LFU (int[][] operators, int k) {
    
    
        // 构建初始化连接
        //链表剩余大小
        this.size = k;
        //获取操作数
        int len = (int)Arrays.stream(operators).filter(x->x[0]==2).count();
        int[] res = new int[len];
        //遍历所有操作
        for(int i=0, j=0; i<operators.length;i++){
    
    
            if(operators[i][0] == 1){
    
    
                set(operators[i][1],operators[i][2]);
            }else{
    
    
                res[j++] = get(operators[i][1]);
            }
        }
        return res;
    }

    //调用函数时更新频率或者val值
    private void update(Node node ,int key, int val){
    
    
        //找到频率
        int freq = node.freq;
        //原频率中删除该节点
        freq_map.get(freq).remove(node);
        //哈希表中该频率已经无节点,直接删除
        if(freq_map.get(freq).isEmpty()){
    
    
            freq_map.remove(freq);
            //若当前频率为最小,最小频率加1
            if(min_freq == freq){
    
    
                min_freq++;
            }
        }
        if(!freq_map.containsKey(freq+1)){
    
    
            freq_map.put(freq+1, new LinkedList<Node>());
        }
        //插入频率加1 的双向链表表头,链表中对应: freq key value
        freq_map.get(freq+1).addFirst(new Node(freq+1, key,val));
        map.put(key,freq_map.get(freq+1).getFirst());
    }

    private void set(int key, int val){
    
    
        if(map.containsKey(key)){
    
    
            update(map.get(key),key,val);
        }else{
    
    
            if(size == 0){
    
    
                int oldkey = freq_map.get(min_freq).getLast().key;
                freq_map.get(min_freq).removeLast();
                if(freq_map.get(min_freq).isEmpty()){
    
    
                    freq_map.remove(min_freq);
                }
                //链表哈希表中删除
                map.remove(oldkey);
            }else{
    
    
                size--;
            }
            min_freq = 1;
            if(!freq_map.containsKey(1)){
    
    
                freq_map.put(1,new LinkedList<Node>());
            }
            freq_map.get(1).addFirst(new Node(1,key,val));
            map.put(key, freq_map.get(1).getFirst());
        }
    }
    private int get(int key){
    
    
        int res = -1;
        if(map.containsKey(key)){
    
    
            Node node =  map.get(key);
            res = node.val;
            update(node,key,res);
        }
        return res;
    }
}

猜你喜欢

转载自blog.csdn.net/YuannaY/article/details/130463931