LRU(Least Recently Used)算法原理

LRU(Least Recently Used)算法原理

一、简介

LRU(Least Recently Used)算法是一种常用的缓存淘汰策略,用于管理计算机系统中的缓存。当缓存满时,需要根据一定的策略淘汰掉一些数据,以便为新的数据腾出空间。LRU 算法的基本思想是:最近最少使用的数据最有可能在未来一段时间内不再被使用,因此应该优先淘汰这些数据。

二、原理概述

LRU 算法的核心是维护一个有序的数据结构,按照数据被访问的时间顺序排列。当缓存满时,移除最久未被访问的数据。LRU 算法可以通过多种数据结构实现,常见的有链表和哈希表结合的数据结构。

三、实现方法

3.1 链表实现

  • 使用一个双向链表存储缓存数据,链表的头部是最近访问的数据,尾部是最久未访问的数据。
  • 每次访问某个数据时,将该数据移动到链表头部。
  • 当缓存满时,移除链表尾部的数据。

3.2 链表实现哈希表+双向链表实现

  • 使用哈希表存储缓存中的数据,以便快速查找。
  • 使用双向链表维护访问顺序,链表的头部是最近访问的数据,尾部是最久未访问的数据。
  • 每次访问某个数据时,通过哈希表定位到链表中的节点,并将该节点移动到链表头部。
  • 当缓存满时,移除链表尾部的节点,并在哈希表中删除相应的条目。

四、示例代码

以下是使用哈希表和双向链表实现 LRU 缓存的示例代码:

import java.util.HashMap;

class LRUCache {
    private class Node {
        int key, value;
        Node prev, next;
        Node(int key, int value) {
            this.key = key;
            this.value = value;
        }
    }

    private final int capacity;
    private HashMap<Integer, Node> map;
    private Node head, tail;

    public LRUCache(int capacity) {
        this.capacity = capacity;
        map = new HashMap<>();
        head = new Node(0, 0);
        tail = new Node(0, 0);
        head.next = tail;
        tail.prev = head;
    }

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

    public void put(int key, int value) {
        if (map.containsKey(key)) {
            Node node = map.get(key);
            remove(node);
        } else if (map.size() == capacity) {
            map.remove(tail.prev.key);
            remove(tail.prev);
        }
        Node node = new Node(key, value);
        insertToHead(node);
        map.put(key, node);
    }

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

    private void insertToHead(Node node) {
        node.next = head.next;
        node.prev = head;
        head.next.prev = node;
        head.next = node;
    }
}

4.1 类定义和成员变量

  • LRUCache 类定义了 LRU 缓存。
  • Node 内部类用于表示双向链表的节点,包含缓存的 key 和 value 以及指向前后节点的指针 prev 和 next。
  • capacity 是缓存的最大容量。
  • map 是哈希表,用于快速查找缓存中的节点。
  • head 和 tail 是双向链表的虚拟头尾节点,方便对链表进行操作。

4.2 构造函数

  • 初始化缓存的容量 capacity。
  • 初始化哈希表 map。
  • 初始化双向链表的虚拟头节点 head 和虚拟尾节点 tail,并将它们连接起来,形成一个空的双向链表。

4.3 获取缓存值

  • get 方法用于获取缓存中的值。
  • 如果 key 存在于哈希表中,获取对应的节点 node,调用 remove 方法将节点从链表中移除,并调用 insertToHead 方法将节点插入链表头部,表示最近使用,然后返回节点的 value。
  • 如果 key 不存在于哈希表中,返回 -1。

4.4 放入缓存值

  • put 方法用于在缓存中插入或更新一个值。
  • 如果 key 已存在于哈希表中,获取对应的节点 node,调用 remove 方法将节点从链表中移除。
  • 如果 key 不存在并且缓存已满,从哈希表中移除最久未使用的节点(即链表尾部节点 tail.prev),并调用 remove 方法将链表尾部节点移除。
  • 创建新的节点 node,调用 insertToHead 方法将新节点插入链表头部,并将新节点添加到哈希表 map 中。

4.5 移除节点

  • remove 方法用于从双向链表中移除一个节点 node。通过调整节点的前后指针,将 node 从链表中断开。

4.6 插入节点到链表头部

  • insertToHead 方法用于将一个节点 node 插入到双向链表的头部。通过调整指针,将 node 插入到虚拟头节点 head 和原第一个节点之间。

五、性能分析

  • 时间复杂度:LRU 缓存的 get 和 put 操作的时间复杂度都是 O(1),因为哈希表的查找和链表的插入/删除操作都是常数时间复杂度。
  • 空间复杂度:空间复杂度是 O(n),其中 n 是缓存的容量。哈希表和链表的空间开销与缓存的容量成线性关系。

六、优缺点

  • 优点
    • LRU 算法简单易实现。
    • 能够较好地淘汰不常使用的数据,提高缓存的命中率。
  • 缺点
    • 在高并发场景下,链表操作可能会成为性能瓶颈。
    • 维护链表的操作会带来一定的开销。

七、实际应用

LRU 算法广泛应用于各类缓存系统,如操作系统的页面置换、数据库的缓存机制、浏览器的缓存管理等。它有效地提高了系统的性能和资源利用率,是缓存淘汰策略中的经典算法之一。

总结

LRU 算法通过淘汰最近最少使用的数据,维护了缓存的高效性。通过哈希表和双向链表的结合,实现了 O(1) 时间复杂度的缓存操作。尽管在高并发场景下可能存在性能瓶颈,但 LRU 算法仍然是实际应用中最为常用和有效的缓存淘汰策略之一。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/635511.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

解决IE11通过主机名访问和IP地址访问,CSS渲染效果不一致问题

软件环境 spingboot:版本2.6.13 浏览器&#xff1a;IE11 问题描述 html用css渲染&#xff0c;浏览器输入IP地址访问&#xff0c;和输入主机名访问&#xff0c;效果不一样&#xff0c;如下图&#xff1a; IP地址访问才是我想要的效果&#xff0c;主机访问菜单半透明向下箭头…

商城项目【尚品汇】02初始项目搭建及其代码提交

1.项目结构 1、首先在本地创建ssh key&#xff1b; DELLLJL MINGW64 ~/Desktop $ ssh-keygen -t rsa -C "自己的qq.com"后面的your_emailyouremail.com改为你在gitee上注册的邮箱&#xff0c;之后会要求确认路径和输入密码&#xff0c;我们这使用默认的一路回车就行…

【区块链】外部应用程序与区块链进行交互

一&#xff0c;外部应用程序与区块链进行交互案例目标与流程 1.1案例目标 掌握FISCO BCOS应用环境的搭建 与使用&#xff08;FISCO BCOSWeBASE&#xff09;掌握基于Java SpringBoot的应 用程序后端项目搭建与开发。掌握应用程序后端与FISCO BCOS 链的交互。掌握应用程序前端…

Golang | Leetcode Golang题解之第108题将有序数组转换为二叉搜索树

题目&#xff1a; 题解&#xff1a; func sortedArrayToBST(nums []int) *TreeNode {rand.Seed(time.Now().UnixNano())return helper(nums, 0, len(nums) - 1) }func helper(nums []int, left, right int) *TreeNode {if left > right {return nil}// 选择任意一个中间位置…

如何通过软件IIC使用MPU6050陀螺仪

目录 1. MPU6050简介 2. MPU6050参数 3. MPU6050硬件电路 4. 代码编写 4.1 MPU6050写寄存器 4.2 MPU6050读寄存器 4.3 初始化 4.4 MPU6050获取ID号 4.5 MPU6050获取数据 1. MPU6050简介 MPU6050是一个6轴姿态传感器&#xff0c;可以测量芯片自身X、Y、Z轴的…

代码随想录--哈希表--有效的字母异位词

题目 给定两个字符串 s 和 t &#xff0c;编写一个函数来判断 t 是否是 s 的字母异位词。 示例 1: 输入: s “anagram”, t “nagaram” 输出: true 示例 2: 输入: s “rat”, t “car” 输出: false 说明: 你可以假设字符串只包含小写字母。 思路 先看暴力的解法&am…

AI智能体|使用扣子Coze基于IDE创建自定义插件

大家好&#xff0c;我是无界生长。 在使用Coze的过程中&#xff0c;有些个性化场景无法通过插件商店已有的插件满足&#xff0c;这个时候就需要通过自定义插件的方式来实现业务需求。下面将通过一个实际案例来简单介绍下如何使用Coze基于IDE创建自定义插件&#xff0c;完成在Co…

JAVA基础Day 1面向对象

目录 包调用包 对象和类多态继承重写与重载 抽象接口接口的声明接口的实现 包 package bao;class FreshJuice{enum FreshJuiceSize{small,medium,lager}FreshJuiceSize size; } public class aa {public static void main(String[] args) {System.out.println("hello&quo…

word如何按照原本页面审阅文档

1 视图-阅读视图 2 视图&#xff0c;自己看&#xff0c;懒得打字了哈哈

基于物联网表计的综合能源管理方案

安科瑞电气股份有限公司 祁洁 acrelqj 摘要&#xff1a;为加快推进国家“双碳”战略和新型能源体系建设&#xff0c;努力实现负荷精准控制和用户精细化管理&#xff0c;按照“政府主导、电网组织、政企协同、用户实施”的指导原则&#xff0c;多地成立市/县级电力负荷管理中…

AI网络爬虫:批量爬取电视猫上面的《庆余年》分集剧情

电视猫上面有《庆余年》分集剧情&#xff0c;如何批量爬取下来呢&#xff1f; 先找到每集的链接地址&#xff0c;都在这个class"epipage clear"的div标签里面的li标签下面的a标签里面&#xff1a; <a href"/drama/Yy0wHDA/episode">1</a> 这个…

Android 观察者模式(OBSERVER)应用详解

文章目录 1、观察者模式设计初衷1.1. 解耦对象之间的依赖关系1.2. 允许动态的依赖关系1.3. 自动通知和更新1.4 设计初衷的详细说明1. 对象之间的解耦2. 动态依赖关系3. 自动更新 2、实现细节2.1. Subject 接口和实现2.2. Observer 接口和实现2.3. 主类 3、主要角色4、关系示意图…

Vue02-黑马程序员学习笔记

一、今日学习目标 1.指令补充 指令修饰符v-bind对样式增强的操作v-model应用于其他表单元素 2.computed计算属性 基础语法计算属性vs方法计算属性的完整写法成绩案例 3.watch侦听器 基础写法完整写法 4.综合案例 &#xff08;演示&#xff09; 渲染 / 删除 / 修改数量 …

创建桌面快捷方式

①点击桌面任务栏中的【开始图标】>点击【所有应用】 ②将【EndNote】图标拖到电脑桌面。

Defog发布Llama-3-SQLCoder-8B,文本转SQL模型,性能比肩GPT-4,准确率超90%,消费级硬件可运行

前言 在计算语言学领域&#xff0c;将自然语言转化为可执行的SQL查询是一个重要的研究方向。这对于让那些没有编程或SQL语法知识的用户也能轻松访问数据库信息至关重要。Defog团队近日发布了基于Llama-3的SQLCoder-8B模型&#xff0c;它在文本转SQL模型领域取得了显著突破&…

【LLM多模态】LLava模型结构和训练过程 | CLIP模型

note CLIP使用了对比学习的方法&#xff0c;即通过正样本&#xff08;匹配的图像-文本对&#xff09;和负样本&#xff08;不匹配的图像-文本对&#xff09;来训练模型。在训练过程中&#xff0c;模型会尝试最大化正样本对的相似度&#xff08;比如通过计算余弦相似度&#xf…

单细胞分析(Signac): PBMC scATAC-seq 聚类

引言 在本教学指南中&#xff0c;我们将探讨由10x Genomics公司提供的人类外周血单核细胞&#xff08;PBMCs&#xff09;的单细胞ATAC-seq数据集。 加载包 首先加载 Signac、Seurat 和我们将用于分析人类数据的其他一些包。 if (!requireNamespace("EnsDb.Hsapiens.v75&qu…

HTTP3

HTTP 状态码&#xff1a;描述了这次HTTP请求是否成功&#xff0c;以及失败的原因。 他们用相应的状态码来描述异常的发现。 常见的状态码 1.200 OK 访问成功。 2.404 NOT Found 客户端请求的资源在服务器这边不存在 URL&#xff1a;ip端口路径查询字符串 3.403 Forbid…

SQL刷题笔记day1

1题目 我的代码&#xff1a; select * from employees order by hire_date desc limit 2,1 标准代码&#xff1a; select * from employees where hire_date (select distinct hire_date from employees order by hire_date desc limit 2,1) 复盘&#xff1a;因为按照入…

vue3插槽solt 使用

背景增加组件的复用性&#xff0c;个人体验组件化还是react 方便。 Vue插槽solt如何传递具名插槽的数据给子组件&#xff1f; 一、solt 原理 知其然知其所以然 Vue的插槽&#xff08;slots&#xff09;是一种分发内容的机制&#xff0c;允许你在组件模板中定义可插入的内容…