P5528.带过期时间的 LRU 缓存

题目

https://codefun2000.com/ide/P5528?pset=zijie

题解(ACM)

java 复制代码
import java.util.*;
import java.io.*;

class LRUcacheWithTTL {

    class Node {
        int key;
        int value;
        long expiretime;
        Node pre;
        Node next;

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

    HashMap<Integer, Node> map = new HashMap<>();

    // 按过期时间从小到大排序(懒删除:旧版本节点留在堆里,用到时再跳过)
    PriorityQueue<Node> pq = new PriorityQueue<>(
        (a, b) -> Long.compare(a.expiretime, b.expiretime)
    );

    int cap;
    Node head = new Node(-1, -1, -1);
    Node tail = new Node(-1, -1, -1);

    LRUcacheWithTTL(int cap) {
        this.cap = cap;
        head.next = tail;
        tail.pre = head;
    }

    public int get(int k, int time) {
        Node node = map.get(k);
        //不存在
        if (node == null) {
            return -1;
        }

        // 存在,过期
        if (time >= node.expiretime) {
            delete(node);
            return -1;
        }
        //存在,没过期,删node + node后移
        moveToLast(node);
        return node.value;
    }

    public void put(int k, int v, int ttl, int time) {

        if (cap <= 0) return;

        Node node = map.get(k);
        Node newnode = new Node(k, v, (long) ttl + time);

        // 已存在,更新map node pq
        if (node != null) {
            delete(node);//删node
            addLast(newnode);//newnode后移
            map.put(k, newnode);//map
            pq.offer(newnode);//pq放入
            rebuildIfNeeded();//更新
            return;
        }

        // 修改点2:容量满时,清理所有已过期节点
        if (map.size() >= cap) {
            while (!pq.isEmpty()) {
                Node cur = pq.peek();

                // 旧版本节点,跳过
                if (map.get(cur.key) != cur) {
                    pq.poll();
                    continue;
                }

                // 最早过期的节点已过期,继续检查下一个
                if (time >= cur.expiretime) {
                    pq.poll();
                    delete(cur);
                    continue;
                }
                break;
            }
        }

        // 仍然满,淘汰 LRU
        if (map.size() >= cap) {
            delete(head.next);
        }

        map.put(k, newnode);
        addLast(newnode);
        pq.offer(newnode);
        rebuildIfNeeded();
    }

    // 堆里旧节点过多时重建,避免超时/超内存
    private void rebuildIfNeeded() {
        if (pq.size() > 2 * map.size() + 16) {
            pq.clear();
            pq.addAll(map.values());
        }
    }


    private void delete(Node node) {
        remove(node);
        map.remove(node.key);
    }

    private void remove(Node node) {
        node.pre.next = node.next;
        node.next.pre = node.pre;
    }

    private void moveToLast(Node node) {
        remove(node);
        addLast(node);
    }

    private void addLast(Node node) {
        Node last = tail.pre;

        last.next = node;
        node.pre = last;

        node.next = tail;
        tail.pre = node;
    }
}

class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        int cap = sc.nextInt();
        int n = sc.nextInt();

        LRUcacheWithTTL lru = new LRUcacheWithTTL(cap);
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < n; i++) {
            String op = sc.next();

            if (op.equals("get")) {
                int a = sc.nextInt();
                int b = sc.nextInt();

                sb.append(lru.get(a, b)).append('\n');

            } else if (op.equals("put")) {
                int a = sc.nextInt();
                int b = sc.nextInt();
                int c = sc.nextInt();
                int d = sc.nextInt();

                lru.put(a, b, c, d);
            }
        }

        System.out.print(sb);
    }
}
相关推荐
瑞雪兆丰年兮7 小时前
JavaWeb-day07-MySQL(DQL&多表设计)
java·开发语言·数据库·sql·mysql·javaweb·web
唐老板7 小时前
Spring Boot 4 默认开虚拟线程,HikariCP 却成瓶颈
java
ShineWinsu10 小时前
对于Qt:QDial常见属性以及信号的解析
开发语言·c++·qt·gui·客户端·旋钮·qdial
Java后端的Ai之路12 小时前
RAG 还是 Agent 自主检索?Claude Code 删掉向量库之后,我把选型讲透了
开发语言·人工智能·prompt·agent·rag
starzy199013 小时前
深入理解Scala高阶函数:函数式编程核心
开发语言·scala
2601_9621066417 小时前
【Java Set综合实战】商品管理系统 + 图书收藏系统|Product、Book自定义类全套代码
java
RuoyiOffice18 小时前
SpringBoot+Vue3 凭证生命周期设计:审核、过账、冲销与反过账怎样保持账目一致
java·spring boot·事务·springboot3·财务系统·会计凭证·ruoyi office
衡石科技18 小时前
兼顾查询速度与数据时效:衡石的缓存和分析加速
缓存·数据缓存·数据时效
2601_9621066418 小时前
HashMap入门|工号-姓名键值存储、两种遍历方式、根据key/value查
java
czq_268671948719 小时前
Python打卡第46天
开发语言·python