一文彻底搞懂基于数组和链表分别实现LRU算法

文章目录

  • 1. LRU算法
  • 2. 基于数组实现LRU算法
  • 3. 基于链表实现LRU算法

1. LRU算法

常见的缓存淘汰策略有三种,分别是:先进先出策略FIFO(First In,First Out)、最少使用策略LFU(Least Frequently Used)、最近最少使用策略LRU(Least Recently Used)

  • 先进先出策略(FIFO):即最先进入缓存的数据最先被淘汰。这种策略类似于队列的工作方式,新数据加入到队列的尾部,而最先进入队列的数据会被淘汰。
  • 最少使用策略(LFU):即最少被访问的数据最先被淘汰。LFU策略根据数据的访问频率来决定淘汰顺序,访问次数最少的数据会被优先淘汰。
  • 最近最少使用策略(LRU):即最近最少被访问的数据最先被淘汰。LRU策略根据数据的访问时间来决定淘汰顺序,最近最久未被访问的数据会被优先淘汰。

LRU算法的工作原理:

  1. 首先,我们定义一个容量为4的整型数组来模拟缓存,初始状态数组为空
  2. 我们开始向数组中查询数据,如果数据存在就返回,数据不存在就插入数据
    2.1 查询数据1:由于缓存为空,将数据1插入到数组头部(下标0)
    2.2 查询数据2:由于缓存中已有数据1,根据LRU算法,将数据2插入到数组头部,将原来的数据1向后位移,数组中的数据是2、1
    2.3 查询数据3和4:类似地,数据3和4被插入到数组的头部,数组中的数据变为4、3、2、1,此时数组达到了缓存的最大容量
  3. 继续访问数据就有两种方式
    3.1 访问缓存中没有的数据,比如5,那么由于此是数组已经满了,所以根据LRU算法定义,我们把数组尾部的数据1剔除,然后把1之前的数据,都向右位移一位,此时数组是:null、4、3、2,然后再将数据5插入到数组的头部,那么此时数组中的数据就是:5、4、3、2
    3.2 访问缓存中已经有的数据,比如2,那么不管此时数组是否已经满了,我们可以在数组中找到数据2,那么我们就先把数组中数据是2的数据删除,然后把2之前的数据显向右位移一位,此时数组中的数据是:null、4、3、1,然后我们再把数据2插入到数组的头部之后,数组中的数据是:2、4、3、1

2. 基于数组实现LRU算法

import java.util.HashMap;
import java.util.Map;/*** 基于数组实现LRU算法*/
public class LRUBaseArray<T> {private static final int DEFAULT_CAPACITY = 8;private int capacity; // 缓存容量private int count; // 缓存中元素数量private T[] value; // 存放元素的数组,模拟缓存private Map<T, Integer> holder; // 存储数据的位置public LRUBaseArray() {this(DEFAULT_CAPACITY);}public LRUBaseArray(int capacity) {this.capacity = capacity;value = (T[]) new Object[capacity];count = 0;holder = new HashMap<>(capacity);}/*** 访问某个值,有的话更新到头部,没有的话尝试插入到头部** @param object 待插入的元素*/public void offer(T object) {if (object == null) {throw new IllegalArgumentException("缓存容器不支持null");}Integer index = holder.get(object);if (index == null) {if (isFull()) {removeAndCache(object);} else {cache(object);}} else {update(index);}}/*** 将新元素插入缓存头部** @param object 待插入的元素*/private void cache(T object) {rightShift(count);value[0] = object;holder.put(object, 0);count++;}/*** 更新已存在元素的位置到缓存头部** @param index 元素在缓存中的位置索引*/private void update(Integer index) {T target = value[index];rightShift(index);value[0] = target;holder.put(target, 0);}/*** 缓存满的情况下,删除尾部元素,并将新元素插入缓存头部** @param object 待插入的元素*/private void removeAndCache(T object) {T key = value[--count];holder.remove(key);cache(object);}/*** 将数组中的元素向右移动一位** @param end 数组边界*/public void rightShift(int end) {for (int i = end - 1; i >= 0; i--) {value[i + 1] = value[i];holder.put(value[i], i + 1);}}/*** 判断缓存是否已满** @return 缓存是否已满*/private boolean isFull() {return count == capacity;}@Overridepublic String toString() {StringBuilder sb = new StringBuilder();for (int i = 0; i < count; i++) {sb.append(value[i]);sb.append(" ");}return sb.toString();}public static void main(String[] args) {LRUBaseArray<Integer> lru = new LRUBaseArray<>();lru.offer(1);lru.offer(2);lru.offer(3);lru.offer(4);lru.offer(5);System.out.println(lru);lru.offer(6);lru.offer(7);lru.offer(8);lru.offer(9);System.out.println(lru);}
}

执行结果

5 4 3 2 
9 8 7 6 

首先将数字1到5依次加入缓存,当缓存容量达到上限4时,数字1被替换出去,然后继续添加数字6到9,此时数字5被替换出去,最终缓存中只剩下数字6到9。

3. 基于链表实现LRU算法

import java.util.HashMap;
import java.util.Map;/*** 基于单链表实现LRU算法* @param <T>*/
public class LRUBaseLinkedList<T> {// 默认链表容量private final static int DEFAULT_CAPACITY = 5;// 头节点private Node<T> head;// 链表长度private int size;// 链表容量private int capacity;// 记录节点的前一个节点,方便删除操作private Map<T, Node<T>> prevNodeMap;public LRUBaseLinkedList() {this(DEFAULT_CAPACITY);}public LRUBaseLinkedList(int capacity) {this.capacity = capacity;this.size = 0;this.head = new Node<>();this.prevNodeMap = new HashMap<>();}/*** 插入元素** @param data 待插入的元素*/public void add(T data) {if (data == null) {throw new IllegalArgumentException("Data cannot be null");}Node<T> prevNode = prevNodeMap.get(data);if (prevNode != null) {deleteElem(prevNode);} else {if (size >= capacity) {deleteLastElem();}}insertElemAtHead(data);}// 删除尾节点private void deleteLastElem() {Node<T> ptr = head;while (ptr.next.next != null) {ptr = ptr.next;}prevNodeMap.remove(ptr.next.data);ptr.next = null;size--;}// 删除当前节点的下一个节点private void deleteElem(Node<T> prevNode) {Node<T> temp = prevNode.next;prevNode.next = temp.next;prevNodeMap.remove(temp.data);temp = null;size--;}// 插入元素到链表头节点private void insertElemAtHead(T data) {Node<T> next = head.next;head.next = new Node<>(data, next);prevNodeMap.put(data, head);size++;}// 定义内部存储结构private static class Node<T> {T data;Node<T> next;Node() {}Node(T data, Node<T> next) {this.data = data;this.next = next;}}@Overridepublic String toString() {StringBuilder sb = new StringBuilder();Node<T> temp = head.next;while (temp != null) {sb.append(temp.data).append(" ");temp = temp.next;}return sb.toString();}public static void main(String[] args) {LRUBaseLinkedList<Integer> list = new LRUBaseLinkedList<>();list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);System.out.println(list);list.add(6);System.out.println(list);list.add(4);System.out.println(list);}
}

执行结果

1 2 3 4 5 
6 1 2 3 4 
4 6 1 2 

在第一次插入元素时,链表中依次添加了1、2、3、4、5这五个元素,并打印了链表中的所有元素。
在第二次插入元素时,由于容量已满,因此删除了链表的尾部元素5,然后将新的元素6插入到了链表头部。
在第三次插入元素时,元素4已经存在于链表中,因此删除了元素4所在的位置,并将其移动到了链表头部。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

董兆祥出席工业废水资源化,开创变废为宝新途径演讲

演讲嘉宾&#xff1a;董兆祥 董事长 河北奥博水处理有限公司 演讲题目&#xff1a;工业废水资源化&#xff0c;开创变废为宝新途径 会议简介 “十四五”规划中提出&#xff0c;提高工业、能源领城智能化与信息化融合&#xff0c;明确“低碳经济”新的战略目标&#xff0c;热…

springcloud:3.2测试超时机制

服务提供者 Openfeign远程调用服务提供者搭建 文章地址http://t.csdnimg.cn/06iz8 PaymentController【控制层】 /*** 测试超时机制** return*/GetMapping("/timeout")public String TimeOut() {try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {…

应用层DDoS防护:理解、必要性与实现策略

一、应用层简介 应用层&#xff0c;也称作第七层&#xff0c;是OSI&#xff08;开放系统互联&#xff09;模型中的最高层。在这一层&#xff0c;数据以特定的应用程序协议格式进行传输&#xff0c;如HTTP、FTP、SMTP等。应用层的主要职责是为用户提供网络服务&#xff0c;如文…

【笔记】Android Telephony 获取SubscriptionManager和TelephonyManager

背景 早期的手机只有单卡 &#xff0c;基本用默认卡&#xff08;代码如下&#xff09;&#xff0c;那么双卡手机的业务逻辑就会存在问题。 //手动搜网的功能案例&#xff0c;根据卡槽/Phone对象直接获取信息private Context mcontext context; private Phone mPhone PhoneF…

LeetCode 560. 和为 K 的子数组

由于题目要求子数组必须连续&#xff0c;也就是需要一个和为K的区间&#xff0c;可以利用前缀和预处理后&#xff0c;枚举找到这些区间段[l,r]&#xff0c;使之满足s[r] - s[l] k。 不理解前缀和的可以先看这里。 class Solution { public:int subarraySum(vector<int>…

MongoDB聚合运算符:$count

文章目录 语法使用举例在$group阶段中使用在$setWindowFields阶段使用 $count聚合运算符返回分组中文档的数量。从5.0开始支持。 语法 { $count: { } }$count不需要参数 使用 $count可以用于下列聚合阶段&#xff1a; $bucket$bucket$group$setWindowFields 在$group阶段中…

【vuex之五大核心概念】

vuex:五大核心概念 一、state状态1.state的含义2.如何访问以及使用仓库的数据&#xff08;1&#xff09;通过store直接访问获取store对象 &#xff08;2&#xff09;通过辅助函数MapState 二、mutations1.作用2.严格模式3.操作流程定义 mutations 对象&#xff0c;对象中存放修…

Freesia 项目引用的依赖

UML图 项目总依赖 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.0</version> </parent> <groupId>com.freesia</groupId> <artifa…

计算机网络_2.1 物理层概述

2.1 物理层概述 一、物理层要实现的功能二、物理层接口特性 B站 深入浅出计算机网络 2.1物理层概述 一、物理层要实现的功能 物理层要实现的功能就是在各种传输媒体上传输比特0和1&#xff0c;进而给上面的数据链路层提供透明传输比特流的服务。 数据链路层“看不见”&#xff…

剑指offer面试题22:链表中倒数第k个节点

面试题22&#xff1a;链表中倒数第k个节点 题目&#xff1a; 实现一种算法&#xff0c;找出单向链表中倒数第 k 个节点。返回该节点的值。 示例&#xff1a; 输入&#xff1a; 1->2->3->4->5 和 k 2 输出&#xff1a; 4思路&#xff1a; 1、求倒数第k个节点的…

设计模式-命令模式(Command Pattern)

承接Qt/C软件开发项目&#xff0c;高质量交付&#xff0c;灵活沟通&#xff0c;长期维护支持。需求所寻&#xff0c;技术正适&#xff0c;共创完美&#xff0c;欢迎私信联系&#xff01; 一、命令模式的说明 命令模式&#xff08;Command Pattern&#xff09;是一种行为设计模式…

跨境代购系统独立站:掌握核心竞争优势,打造专业国际购物体验

跨境代购系统独立站&#xff08;获取代购系统独立站演示&#xff09;的核心竞争优势可能包括&#xff1a; 独立性&#xff1a;独立站不依赖于任何第三方电商平台&#xff0c;拥有自己的域名和网站空间&#xff0c;可以自主控制网站的设计和内容。灵活性&#xff1a;独立站不受…

springboot基于web的网上摄影工作室的开发与实现论文

网上摄影工作室 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了网上摄影工作室的开发全过程。通过分析网上摄影工作室管理的不足&#xff0c;创建了一个计算机管理网上摄影工作室的方案。文章介绍了网上摄影工…

微信小程序云开发教程——墨刀原型工具入门(动态组件)

引言 作为一个小白&#xff0c;小北要怎么在短时间内快速学会微信小程序原型设计&#xff1f; “时间紧&#xff0c;任务重”&#xff0c;这意味着学习时必须把握微信小程序原型设计中的重点、难点&#xff0c;而非面面俱到。 要在短时间内理解、掌握一个工具的使用&#xf…

0基础跨考计算机|408保姆级全年计划

我也是零基础备考408&#xff01; 虽说是计算机专业&#xff0c;但是本科一学期学十几门,真的期末考试完脑子里什么都不进的...基本都是考前一周发疯学完水过考试...&#x1f605; 想要零基础跨考可以直接从王道开始&#xff01;跟教材一点一点啃完全没必要&#x1f978; 现在…

八股文打卡day25——数据库(2)

面试题&#xff1a;讲一下事务的四大特性&#xff1f; 我的回答&#xff1a; ACID A代表原子性&#xff0c;一个事务代表一个业务&#xff0c;要么全部都完成&#xff0c;要么全部都不完成。如果事务执行失败了&#xff0c;会回滚到最原来的状态。 C代表一致性&#xff0c;举…

【STM32】江科大STM32学习笔记汇总(50)

00. 目录 文章目录 00. 目录01. STM32学习笔记汇总02. 相关资料下载03. 附录 01. STM32学习笔记汇总 【STM32】STM32学习笔记-课程简介(01) 【STM32】STM32学习笔记-STM32简介(02) 【STM32】STM32学习笔记-软件安装(03) 【STM32】STM32学习笔记-新建工程(04) 【STM32】STM…

venv、pip、conda、anaconda、miniconda的区别和优缺点,和彻底清除python多余的环境

virtualenv(venv) 这是一个虚拟环境管理器&#xff0c;它可以让你每个项目甚至每个脚本配置一个自定义的Python解释器环境&#xff0c;这最大的好处是我可以不污染开发环境。​ pip pip 是 Python 最常用的包管理器&#xff0c;它能自动处理依赖 。 conda 如果说venv是虚拟…

CSS特性

小技巧&#xff1a;在调试工具中&#xff0c;css样式上看层叠&#xff0c;下看继承。 1、层叠性 相同的属性会被覆盖&#xff0c;不同的属性会叠加 2、继承性 3、优先级 基于不同种类的选择器的匹配规则。 通配符 < 标签 < 类选择器 < id选择器 < 行内样式 <…

大语言模型(LLM)技术名词表(一)

LLMs on a Phone&#xff1a;指在手机设备上运行的大型语言模型。 Scalable Personal AI&#xff1a;指用户可以在个人设备上对AI模型进行微调的技术。 Responsible Release&#xff1a;发布AI模型时考虑社会、法律和伦理影响的做法。 Multimodality&#xff1a;AI模型能处理…