华为OD机试 - 文件缓存系统——优先队列解法

华为OD机试 - 文件缓存系统——优先队列解法

题目描述

题目描述链接🔗

题目分析

  • 这题需要我们实现一个LFUCache的自定义数据结构,根据题意,需要分别定义一个putget方法,用于存储缓存和获取缓存。
  • 本题难点在于put方法中,如果当前缓存空间如果不够存放新加入的缓存,则需要将已有缓存根据条件进行删除,直到可以存放新加的缓存。这里的条件是两个维度,分别是访问次数(少->多)、访问时间(老->新)。
  • 我们需要一个数据结构来通过这两个条件,对加入的缓存进行排序,方便我们快速获取要删除的缓存,这里我们选择优先队列进行存储。并将访问次数和访问时间这两个属性将缓存封装为Cache的数据结构,通过这两个属性对Cache进行排序即可。

代码解析

代码已AC,细节见下面注释👇🏻

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int capacity = Integer.parseInt(sc.nextLine());
        int n = Integer.parseInt(sc.nextLine());
        LFUCache lfuCache = new LFUCache(capacity);
        //读取后续n个操作
        for (int i = 1; i <= n; i++) {
            String[] str = sc.nextLine().split(" ");
            //这里通过数组长度判断put/get操作,也可以通过第0个索引的字符串判断
            if (str.length == 3) {
                lfuCache.put(str[1], Integer.parseInt(str[2]), i);
            } else if (str.length == 2) {
                lfuCache.get(str[1], i);
            }
        }
        lfuCache.printCaches();
    }

    private static class LFUCache {
        private static int capacity;

        //缓存中剩余的空间
        private static int freeCap;

        private static final HashMap<String, Cache> name2File = new HashMap<>();

        //按访问次数从少到多,访问时间从老到新(从小到大)对缓存排序
        private static final PriorityQueue<Cache> pq = new PriorityQueue<>((a, b) ->
                a.visitCount == b.visitCount ? (a.visitTime - b.visitTime)
                        : a.visitCount - b.visitCount);

        public LFUCache(int capacity) {
            LFUCache.capacity = capacity;
            freeCap = capacity;
        }

        public void put(String fileName, int fileSize, int visitTime) {
            if (name2File.containsKey(fileName) || fileSize > capacity) {
                return;
            }
            Cache cache = new Cache(fileName, fileSize, visitTime);
            if (freeCap < cache.size) {
                //LFUCache剩余空间不够时,需要将已有缓存移除,直到满足当前新缓存cache
                while (freeCap < cache.size) {
                    if (pq.size() == 0) {
                        return;
                    }
                    //移除时需要将map和pq中的同一个对象都移除
                    Cache removed = pq.poll();
                    name2File.remove(removed.name);
                    freeCap += removed.size;
                }
            }
            //新加入时也要分别存储
            name2File.put(fileName, cache);
            pq.offer(cache);
            freeCap -= fileSize;
        }

        public void get(String fileName, int visitTime) {
            Cache cache = name2File.get(fileName);
            if (cache == null) {
                return;
            }
            //更新cache访问次数和时间
            cache.visitCount++;
            cache.visitTime = visitTime;
            //这个cache在map和优先队列中是同一个对象,修改了上面两个属性后优先队列并未重新排序
            //因此这里通过将cache重新加入队列的方式进行重新排序
            pq.remove(cache);
            pq.add(cache);
        }

        public void printCaches() {
            Collection<Cache> values = name2File.values();
            //没有缓存时,打印NONE
            if (values.size() == 0) {
                System.out.println("NONE");
            }
            StringJoiner sj = new StringJoiner(",");
            //剩余缓存字典序排序输出
            values.stream().sorted((a, b) -> a.name.compareTo(b.name))
                    .forEach(a -> sj.add(a.name));
            System.out.println(sj);
        }
    }

    private static class Cache {
        String name;
        int size;
        int visitCount;
        //访问时间,值越大访问时间越新
        int visitTime;

        public Cache(String name, int size, int visitTime) {
            this.name = name;
            this.size = size;
            this.visitCount = 0;
            this.visitTime = visitTime;
        }
    }
}

复杂度分析

时间复杂度:O(nlogn),优先队列一次put或get操作需要O(logn)的时间,n个元素共需要O(nlogn),n是操作数量。
空间复杂度:O(n),n是操作数量,主要是一个哈希表和优先队列所占空间。

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值