[Daily Practice] -最不经常使用(LFU)缓存的数据结构

题目描述

设计并实现最不经常使用(LFU)缓存的数据结构。它应该支持以下操作:get 和 put。
get(key) - 如果键存在于缓存中,则获取键的值(总是正数),否则返回 -1。
put(key, value) - 如果键不存在,请设置或插入值。当缓存达到其容量时,它应该在插入新项目之前,使最不经常使用的项目无效。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,最近最少使用的键将被去除。
该题目来源于leetcode,点击进入

读题

示例:

LFUCache cache = new LFUCache( 2 /* capacity (缓存容量) */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // 返回 1
cache.put(3, 3);    // 去除 key 2
cache.get(2);       // 返回 -1 (未找到key 2)
cache.get(3);       // 返回 3
cache.put(4, 4);    // 去除 key 1
cache.get(1);       // 返回 -1 (未找到 key 1)
cache.get(3);       // 返回 3
cache.get(4);       // 返回 4

设计如下:
在这里插入图片描述

代码

LFUCache 类

public class LFUCache {
    private int cap; // 缓存总容量
    private int size; // 当前缓存使用量
    private HashMap<Integer, Node> cache; // 缓存Map
    private HashMap<Integer, ArrayList<Node>> freqNodes; // 记录使用次数

    // 打印当前缓存内容 测试用
    public void printCache(){
        for (Map.Entry<Integer, Node> entry : cache.entrySet()){
            System.out.print(entry.getKey()+" ");
        }
        System.out.println();
    }
    public LFUCache(int capacity) {
        this.cap = capacity;
        this.cache = new HashMap<Integer, Node>(capacity);
        this.freqNodes = new HashMap<Integer, ArrayList<Node>>();
    }

    public int get(int key) {
        Node node = cache.get(key);
        if (node == null) {
            return -1;
        }
        node.freq++;
        // 次数有变化,需要更新记录使用次数的Map
        adjustPosition(node);
        return node.value;
    }

    private void adjustPosition(Node node) {
        if (freqNodes.containsKey(node.freq - 1)) {
            freqNodes.get(node.freq - 1).remove(node);
            // list为空时需要将它从记录使用次数的map中移除
            if (freqNodes.get(node.freq - 1).size() == 0) {
                freqNodes.remove(node.freq - 1);
            }
        }
        // 将node加到新的list,如果不存在需要新建
        if (freqNodes.containsKey(node.freq)) {
            freqNodes.get(node.freq).add(node);
        } else {
            ArrayList<Node> newNodes = new ArrayList<Node>();
            newNodes.add(node);
            freqNodes.put(node.freq, newNodes);
        }

    }


    public int getMinReq() {
        int req  = 0;
        boolean flag = true;
        // 循环获取最小key,没有什么好说的
        for (Map.Entry<Integer, ArrayList<Node>> entry : freqNodes.entrySet()) {
            if(flag){
                req = entry.getKey();
                flag = false;
            }else{
                req = Integer.min(req, entry.getKey());
            }
        }
        return rep;
    }

    public void put(int key, int value) {
        // 容量为0无法存储
        if (cap == 0) return;
        // 判断是否已经存在
        Node node = cache.get(key);

        if (node == null) {
            // 不存在的情况,需要判断缓存是否已满,没有满,直接加入,若满了,需要找到使用次数最少最近调用时间最靠后的
            if (size == cap) {
            	// 找到使用次数最少且最近一次使用时间最长的元素
                int remove = getMinReq();
                int k = freqNodes.get(remove).get(0).key;
                // 将找到的元素从原来的list中删除
                freqNodes.get(remove).remove(0);
                // 从缓存中删除
                cache.remove(k);
                node = new Node(key,value);
                // 新增时,需要更新记录使用次数的Map
                adjustPosition(node);
                cache.put(key, node);
            } else {
                Node newNode = new Node(key, value);
                // 新增时,需要更新记录使用次数的Map
                adjustPosition(newNode);// 加入缓存
                cache.put(key, newNode);
                // 当前缓存大小增加
                size++;
            }

        } else {
            // 已存在的情况,更新调用次数,更新值
            node.value = value;
            node.freq++;
            // 次数有变化,需要更新记录使用次数的Map
            adjustPosition(node);
        }
    }


}

Node 类

class Node {
    int key;
    int value;
    int freq = 0;

    public Node(int key, int value) {
        this.key = key;
        this.value = value;
    }

    public Node() {

    }
}

测试

public class Test {
    public static void main(String[] args) {
        LFUCache lfuCache = new LFUCache(5);
        lfuCache.put(1,1);
        lfuCache.put(2,2);
        lfuCache.put(3,3);
        lfuCache.put(4,4);
        lfuCache.put(5,5);
        System.out.println(lfuCache.get(1));
        System.out.println(lfuCache.get(2));
        System.out.println(lfuCache.get(4));
        System.out.println(lfuCache.get(5));
        lfuCache.put(6,6);
        System.out.println("push(6,6)此时cache:");
        lfuCache.printCache();
        System.out.println(lfuCache.get(3));
        System.out.println(lfuCache.get(6));
        System.out.println("================此时剩余12456 均只调用一次 1距离上次调用时间最久================");
        lfuCache.put(7,7); // 此时应该为 24567
        System.out.println("push(7,7),此时cache:");
        lfuCache.printCache();
        System.out.println(lfuCache.get(1));// 应为 -1


    }
}
  • 运行结果:
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值