首页 > 其他分享 >146. LRU 缓存

146. LRU 缓存

时间:2024-11-19 19:43:56浏览次数:3  
标签:146 node 缓存 value 链表 tail LRU key 节点

https://leetcode.cn/problems/lru-cache/description/?envType=study-plan-v2&envId=top-100-liked
最近最久未使用,显然我们需要维护一个使用队列,最近使用过的在队尾,未使用过的靠近队首
并且他要求函数 get 必须以 O(1) 的平均时间复杂度运行显然我们需要用到hash
put 必须以 O(1)的平均时间复杂度运行显然只有链表能做到,并且我们选择双向链表实现
为什么用双向链表而不是单向链表?
将某个节点移动到链表头部或者将链表尾部节点删去,都要用到删除链表中某个节点这个操作。你想要删除链表中的某个节点,需要找到该节点的前驱节点和后继节点。对于寻找后继节点,单向链表和双向链表都能通过 next 指针在O(1)时间内完成;对于寻找前驱节点,单向链表需要从头开始找,也就是要O(n)时间,双向链表可以通过前向指针直接找到,需要O(1)时间。综上,要想在O(1)时间内完成该操作,当然需要双向链表.
public class LRUCache {
    class Node{
        int key;
        int value;
        Node prev;
        Node next;
        public Node(int key, int value) {
            this.key = key;
            this.value = value;
            this.prev = null;
            this.next = null;
        }
    }
    int capacity;//容量,最大可以存储多少个节点
    int currSize;//当前存储的节点数
    Node head, tail;//头节点,尾节点
    Map<Integer, Node> map = new HashMap<>();//key-value

    public LRUCache(int capacity) {
        this.capacity = capacity;
    }

    public int get(int key) {
        if(map.containsKey(key)){
            Node node = map.get(key);
            removeNode(node);
            return node.value;
        }
        return -1;
    }

    public void put(int key, int value) {
        if(map.containsKey(key)){
            Node node = map.get(key);
            node.value = value;
            removeNode(node);
        } else {
            if(currSize == capacity){
                map.remove(head.key);
                removeNode(head);
                /*if(capacity == 1) {//当前只有一个节点
                    head.key = key;
                    head.value = value;
                } else {//当前节点不是尾节点,则他会被放到尾节点
                    tail.key = key;
                    tail.value = value;
                }*/
                tail.key = key;
                tail.value = value;
                map.put(key, tail);
            } else if(currSize < capacity){
                Node node = new Node(key, value);
                map.put(key, node);
                if(currSize == 0) {//当前没有节点
                    head = node;
                    tail = node;
                } else {
                    tail.next = node;
                    node.prev = tail;
                    tail = node;
                }
                currSize++;
            }

        }
    }

    //将节点node移动到链表尾
    public void removeNode(Node node){
        if(node.next == null) return;
        if(node.prev == null) {//当前节点是头节点
            head = node.next;
            node.next.prev = null;
            node.next = null;
            tail.next = node;
            node.prev = tail;
            tail = node;
        } else {//当前节点不是头节点和尾节点
            node.prev.next = node.next;
            node.next.prev = node.prev;
            node.next = null;
            node.prev = tail;
            tail.next = node;
            tail = node;
        }
    }

    public static void main(String[] args) {
        String[] str = {"LRUCache","put","get","put","get","get"};
        int[][] arr = {{1},{2,1},{2},{3,2},{2},{3}};
        LRUCache lruCache = null;
        List<Integer> list = new ArrayList<>();
        for(int i = 0; i < str.length; i++) {
            if(str[i].equals("LRUCache")) {
                lruCache = new LRUCache(arr[i][0]);
            }
            if(str[i].equals("put")) {
                lruCache.put(arr[i][0], arr[i][1]);
            }
            if(str[i].equals("get")) {
                list.add(lruCache.get(arr[i][0]));
                continue;
            }
            list.add(null);
        }
        System.out.println(list);
    }
}

标签:146,node,缓存,value,链表,tail,LRU,key,节点
From: https://blog.csdn.net/release_lonely/article/details/143893892

相关文章

  • MS5146T/MS5147T/MS5148T——2kSPS、24bit Σ-Δ ADC
    产品简述MS5146T/MS5147T/MS5148T是适合高精度、低成本测量应用的24bit模数转换器。其内部集成低噪声可编程增益放大器、高精度Δ-Σ模数转换器和内部振荡器。MS5147T和MS5148T内部还集成低温漂基准和两路匹配的可编程电流源。M......
  • 04高可用高并发(D1_高并发 - D1_缓存)
    目录学习前言一、缓存简介关键词-命中率缓存介质缓存淘汰算法哪里用了缓存二、缓存应用和实现1.缓存实现-本地缓存1.1.成员变量或局部变量实现1.2.静态变量实现2.EhcacheGuavaCache3.缓存实现-分布式缓存缓存实现方式-注解方式Spring注解缓存用户自......
  • 建立函数及其参数的结果缓存
    fromfunctoolsimportwrapsimporttimeclassCacheManager:def__init__(self):self._cache={}defget_cache_obj(self,key):"""获取缓存对象"""returnself._cache.get(key)defadd_cache_obj(......
  • 【vue】项目迭代部署后 自动清除浏览器缓存
    前言:vue项目打包部署上线后,因浏览器缓存问题,导致用户访问的依旧是上个迭代批次的旧资源,需要用户手动清除缓存才能更新至最新版本,影响用户体验。解决方法:html根文件添加以下标签<metahttp-equiv="pragma"content="no-cache"><metahttp-equiv="cache-control"con......
  • Memcached&Redis构建缓存服务器 (主从,持久化,哨兵)
    许多Web应用都将数据保存到RDBMS中,应用服务器从中读取数据并在浏览器中显示。但随着数据量的增大、访问的集中,就会出现RDBMS的负担加重、数据库响应恶化、网站显示延迟等重大影响。Memcached/redis是高性能的分布式内存缓存服务器,通过缓存数据库查询结果,减少数据库访问次数,......
  • 一看就懂的 UniApp 数据缓存 API:一篇文章带你玩转本地存储!
    UniApp数据缓存API全面解析与最佳实践在多平台跨端开发中,数据缓存是不可或缺的功能。UniApp提供了一套强大的数据缓存API,支持本地数据的存储、读取、删除和管理,适用于多种开发场景。本文将详细介绍这些API的功能、参数及使用方法,并分享一些实际开发中的应用技巧。......
  • 缓存与数据库不一致的解决方案:深入理解与实践
    目录前言缓存与数据库不一致的原因缓存与数据库交互的基本策略常见的缓存与数据库不一致解决方案方案一:读写穿透模式方案二:Cache-Aside模式方案三:先删除缓存,再更新数据库方案四:先更新数据库,再删除缓存方案五:异步更新缓存数据不一致的经典场景与应对策略总结前言在分......
  • 在 PowerShell 中,执行 ipconfig /flushdns 是清除本地 DNS 缓存的标准方式。PowerShel
    在PowerShell中,执行ipconfig/flushdns是清除本地DNS缓存的标准方式。如果你希望在PowerShell脚本中实时清理DNS缓存,你可以直接执行ipconfig/flushdns命令,并输出一些提示信息来确认操作已完成。PowerShell实时清理DNS缓存代码:powershellCopyCode#执行ipconf......
  • 使用 Infinispan 缓存功能支持多个 Redis 数据库
    使用Infinispan缓存功能支持多个Redis数据库    在Infinispan15中,我们提供了大量命令,可以在不更改代码的情况下将Redis服务器替换为Infinispan。在本教程中,您将了解Infinispan缓存别名如何帮助您将多个Redis数据库的Redis服务器替换为Infinispan关键要点:什......
  • DDCA —— 缓存一致性
    1.多处理器内存组织结构1.1SMP/集中式共享内存集中式共享内存多处理器(Centralizedshared-memorymultiprocessor)或对称共享内存多处理器(Symmetricshared-memorymultiprocessor,SMP)多个处理器连接到一个集中式内存——因为所有处理器看到的是相同的内存结构->即统一内......