高级java每日一道面试题-2024年9月30日-算法篇-LRU是什么?如何实现?
如果有遗漏,评论区告诉我进行补充
面试官: LRU是什么?如何实现?
我回答:
LRU(Least Recently Used)是一种常用的缓存淘汰策略,用于在缓存满时决定哪些数据应该被移除。LRU算法的基本思想是:当缓存达到其容量上限时,最近最少使用的数据会被优先淘汰。这种策略假设最近使用的数据在未来也会被频繁访问。
LRU算法概述
LRU算法是一种缓存淘汰策略,其核心思想是:如果一个数据在最近一段时间没有被访问到,那么在未来被访问的可能性也很小。因此,当缓存空间已满时,LRU算法会选择最近最少使用的数据进行淘汰。
LRU算法广泛应用于操作系统中的页面置换、数据库查询优化、Web缓存等场景,是最大化缓存命中率的有效手段之一。
LRU算法的实现原理
LRU的实现
LRU的实现通常需要一个数据结构来同时支持快速查找和插入/删除操作。常用的数据结构是哈希表(HashMap)和双向链表(Doubly Linked List)的结合体。
数据结构
- 哈希表:用于快速查找缓存中的元素。
- 双向链表:用于维护元素的访问顺序,最近访问的元素放在链表头部,最久未访问的元素放在链表尾部。
基本操作
-
插入:
- 如果新插入的键已经在缓存中,则更新其值,并将其移动到链表头部。
- 如果新插入的键不在缓存中,且缓存已满,则移除链表尾部的元素,并将新元素插入到链表头部。
-
访问:
- 如果访问的键在缓存中,则将其移动到链表头部。
- 如果访问的键不在缓存中,则返回null或其他默认值。
-
删除:
- 如果删除的键在缓存中,则从链表和哈希表中移除该元素。
- 如果删除的键不在缓存中,则不进行任何操作。
LRU算法的实现需要满足以下几个要求:
- 查找快:能够迅速找到缓存中的数据。
- 插入快:能够快速地将新数据插入到缓存中。
- 删除快:能够高效地删除缓存中的数据。
- 维护顺序:需要维护数据的访问顺序,以便在缓存空间不足时淘汰最近最少使用的数据。
代码实现
下面是一个使用Java实现LRU缓存的示例:
import java.util.HashMap;
import java.util.Map;public class LRUCache<K, V> {private final int capacity;private final Map<K, Node<K, V>> map;private final DoublyLinkedList<K, V> list;public LRUCache(int capacity) {this.capacity = capacity;this.map = new HashMap<>();this.list = new DoublyLinkedList<>();}public V get(K key) {if (map.containsKey(key)) {Node<K, V> node = map.get(key);list.moveToHead(node); // 将访问的节点移到链表头部return node.value;}return null;}public void put(K key, V value) {if (map.containsKey(key)) {Node<K, V> node = map.get(key);node.value = value; // 更新节点的值list.moveToHead(node); // 将更新的节点移到链表头部} else {if (map.size() >= capacity) {Node<K, V> removedNode = list.removeTail(); // 移除链表尾部的节点map.remove(removedNode.key); // 从哈希表中移除对应的键}Node<K, V> newNode = new Node<>(key, value);list.addHead(newNode); // 将新节点添加到链表头部map.put(key, newNode); // 在哈希表中添加新的键值对}}private static class Node<K, V> {K key;V value;Node<K, V> prev;Node<K, V> next;Node(K key, V value) {this.key = key;this.value = value;}}private static class DoublyLinkedList<K, V> {private Node<K, V> head;private Node<K, V> tail;public void addHead(Node<K, V> node) {if (head == null) {head = tail = node;} else {node.next = head;head.prev = node;head = node;}}public void moveToHead(Node<K, V> node) {if (node == head) return; // 如果节点已经是头结点,则无需移动removeNode(node);addHead(node);}public Node<K, V> removeTail() {if (tail == null) return null;Node<K, V> node = tail;removeNode(tail);return node;}private void removeNode(Node<K, V> node) {if (node.prev != null) {node.prev.next = node.next;} else {head = node.next;}if (node.next != null) {node.next.prev = node.prev;} else {tail = node.prev;}node.prev = null;node.next = null;}}public static void main(String[] args) {LRUCache<Integer, String> cache = new LRUCache<>(2);cache.put(1, "one");cache.put(2, "two");System.out.println(cache.get(1)); // 输出: onecache.put(3, "three"); // 移除最近最少使用的 2System.out.println(cache.get(2)); // 输出: nullcache.put(4, "four"); // 移除最近最少使用的 1System.out.println(cache.get(1)); // 输出: nullSystem.out.println(cache.get(3)); // 输出: threeSystem.out.println(cache.get(4)); // 输出: four}
}
解释
-
LRUCache 类:
capacity:缓存的最大容量。map:哈希表,用于存储键和对应的节点。list:双向链表,用于维护节点的访问顺序。
-
get 方法:
- 如果键存在于缓存中,将对应的节点移动到链表头部,并返回其值。
- 如果键不存在于缓存中,返回null。
-
put 方法:
- 如果键已经存在于缓存中,更新其值并将节点移动到链表头部。
- 如果键不存在于缓存中且缓存已满,移除链表尾部的节点,并将新节点添加到链表头部。
- 如果键不存在于缓存中且缓存未满,直接将新节点添加到链表头部。
-
Node 类:
- 表示双向链表中的一个节点,包含键、值以及前驱和后继指针。
-
DoublyLinkedList 类:
- 实现了双向链表的基本操作,包括添加节点到头部、移动节点到头部、移除节点等。
LRU算法的性能分析
LRU算法的性能主要取决于哈希表和双向链表的操作效率。由于哈希表的查找、插入和删除操作的时间复杂度都是O(1),双向链表的插入、删除和移动操作的时间复杂度也都是O(1)(在已知节点位置的情况下),因此LRU算法的整体时间复杂度可以认为是O(1)。
然而,需要注意的是,在实际应用中,由于哈希表的冲突和链表节点的移动等操作,LRU算法的实际性能可能会受到一定影响。此外,当缓存数据量很大时,哈希表和链表的内存开销也需要考虑。
LRU算法的改进和优化
针对LRU算法的不足,有一些改进和优化方法:
- LRU-K算法:将“最近使用过1次”的判断标准扩展为“最近使用过K次”,以减少缓存污染问题。LRU-K算法需要多维护一个队列来记录所有缓存数据被访问的历史。
- Two Queues(2Q)算法:使用两个缓存队列,一个是FIFO队列,一个是LRU队列。新数据先放入FIFO队列,当数据再次被访问时,将其移到LRU队列。这种算法结合了FIFO和LRU的优点。
- MQ算法:根据访问频率将数据划分为多个队列,不同的队列具有不同的访问优先级。新数据放入最低优先级的队列,当数据的访问次数达到一定次数时,将其提升到更高优先级的队列。
总结
综上所述,LRU算法是一种高效且广泛应用的缓存淘汰策略。在Java中,可以通过使用哈希表和双向链表的组合来实现LRU缓存。同时,也需要根据实际应用场景和需求对LRU算法进行改进和优化。
相关文章:
高级java每日一道面试题-2024年9月30日-算法篇-LRU是什么?如何实现?
如果有遗漏,评论区告诉我进行补充 面试官: LRU是什么?如何实现? 我回答: LRU(Least Recently Used)是一种常用的缓存淘汰策略,用于在缓存满时决定哪些数据应该被移除。LRU算法的基本思想是:当缓存达到其容量上限时࿰…...
CSS选择器的全面解析与实战应用
CSS选择器的全面解析与实战应用 一、基本选择器1.1 通配符选择器(*)2.标签选择器(div)1.3 类名选择器(.class)4. id选择器(#id) 二、 属性选择器(attr)三、伪…...
vue3自动暴露element-plus组件的ref
自动暴露子组件的方法,注意在TS下,需要自己声明类型,我这里全用any代替了 <template><el-button click"getFocus">获得焦点</el-button><com ref"comRef" /> </template><script setup…...
龙芯+FreeRTOS+LVGL实战笔记(新)——10蜂鸣器嘀嘀嘀
本专栏是笔者另一个专栏《龙芯+RT-Thread+LVGL实战笔记》的姊妹篇,主要的区别在于实时操作系统的不同,章节的安排和任务的推进保持一致,并对源码做了完善与优化,各位可以先到本人主页下去浏览另一专栏的博客列表(目前已撰写36篇,图1所示),再决定是否订阅。此外,也可以…...
微信小程序-数据模型与动态赋值
首先新建一个小程序项目. 这边有创建基础项目的流程:从0新建一个微信小程序实现一个简单跳转_小白开发小程序源代码-CSDN博客 一共两步: 1.建立页面的 数据模型 和 默认赋值: 默认赋值: 2.接收输入框的新文案,动态替换上面的文案展示 //文件 testUI.js增加方法:onInputChan…...
【Redis】Linux下安装配置及通过C++访问Redis
文章目录 一、Linux Centos 7.0版本下的安装及配置二、通过C访问Redis 一、Linux Centos 7.0版本下的安装及配置 通过源来安装,此次安装的版本为 redis 5.0 的,要通过其他源进行安装,首先安装 scl 源 yum install centos-release-scl-rh再安…...
Python 入门教程(4)数据类型 | 4.7、元组
文章目录 一、元组1、定义2、创建3、访问元组元素4、遍历元组5、 前言: 在Python编程中,元组(tuple)是一种内置的数据结构,它提供了一种存储多个项目(元素)的方式,这些项目可以是不同…...
Temu正在吸引越来越多的亚马逊卖家,这个市场Temu蝉联下载榜首
近年来,全球电商市场竞争愈发激烈,各大平台纷纷使出浑身解数,以期在激烈的市场竞争中脱颖而出。 一个来自中国的新兴电商平台——Temu,凭借其独特的市场策略和迅猛的发展势头,正在吸引越来越多的亚马逊卖家。Temu为美国…...
设计原则模式概览
前言 架构设计是软件系统稳定的核心因素,也是程序员晋级架构师的核心因素,建议日常开发过程中针对设计进行深挖与思考 核心 分清楚哪些是稳定的,哪些是变化的(一定有稳定跟变化的成分); 捋清楚哪些是类设计…...
高级主题:接口性能测试与压力测试
在现代软件开发中,确保接口的性能和稳定性是非常重要的。随着用户数量的增加,接口需要能够承受高并发请求,从而保证良好的用户体验。本篇文章将介绍如何使用 Python 工具 Locust 进行接口性能测试和压力测试,分析测试结果…...
python绘制图像
柱状图 import os# 输入想要存储图像的路径 os.chdir(D:)import matplotlib.pyplot as plt import numpy as np # 改变绘图风格 import seaborn as snssns.set(color_codesTrue)cell [gen7, xgspon, 3081GB, vettel, totalplay, other] pvalue [21, 20, 18, 13, 7, 34]width…...
如何修复变砖的手机并恢复丢失的数据
您可能之前听说过“变砖”,但您知道什么是变砖手机吗?正如许多论坛中经常提出的问题一样,我如何知道我的手机是否变砖了?好吧,手机变砖主要有两种类型,即软件变砖和硬变砖。软变砖手机意味着重启后您仍然可…...
服务器使用了代理ip,遇到流量攻击,会对服务器有影响吗
当服务器使用代理IP并遭遇流量攻击(如DDoS攻击)时,仍然会对服务器产生影响。以下是关于这种情况的一些详细分析: 1. 流量攻击的性质 流量攻击的目的是通过发送大量请求来耗尽目标服务器的资源或带宽,导致服务中断或不…...
从存储到人工智能洞察: 利用 MinIO 和 Polars 简化数据管道
将 MinIO 的高性能、可扩展企业对象存储的强大功能与 Polars(闪电般快速的 DataFrame 库)的快速内存数据处理功能相结合,可以显著提高数据管道的性能。在 AI 工作流中尤其如此,其中预处理大型数据集和执行特征选择是关键步骤。在这…...
只需要 1 分钟语音数据实现声音克隆
只需要 1 分钟语音数据实现声音克隆 GPT-SoVITS 是一个基于少量语音数据(1 分钟左右)即可训练出高质量 TTS(文本转语音)模型的开源项目,提供少样本语音克隆能力。目前该开源项目已经获得了 33.2k 的 Star!…...
OpenEuler虚拟机安装保姆级教程 | 附可视化界面
0x00 系统介绍 在 2019 年 7 月 19 日,华为宣布要在年底正式开源 openEuler 操作系统;在半年后的 12 月 31 日,华为正式开源了 openEuler 操作系统,邀请社区开发者共同来贡献。 一年后,截止到 2020 年12 月 25日&…...
表格控件QTableWidget
下面说一下表格的常用方法 行列数目、行表头、列表头 行表头:就是表格控件的第一行,用于设置每一列的标题 列表头:就是表格控件的第一列,用于设置每一行的标题,通常缺省则默认显示行号 设置和获取行列的数目 在添…...
LeetCode236题:二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖…...
虚谷中使用PL/SQL改变模式下所有表的大小写
一、将表名转换为小写 1、原理和思路 首先,我们需要查询出指定模式下的所有表名,在xugu中,数据字典dba_tables包含了当前库下的所有表信息,我们可以使用游标(CURSOR)来遍历这些表名。 2、代码示例如下&am…...
数据挖掘的基本步骤和流程解析:深入洞察与策略实施
一、引言 在数据时代的浪潮中,数据挖掘技术已成为企业洞察市场、优化运营和驱动创新的利器。 它融合了统计学、机器学习、数据库管理和人工智能等领域的先进技术,旨在从海量数据中 提取有价值的信息。 本文将深入探讨数据挖掘的六个基本步骤,…...
小熊派gd32f303实战指南(9)— 硬件I2C驱动AT24C02 EEPROM从零到一
1. 硬件I2C与AT24C02基础认知 第一次接触硬件I2C时,我也被那些专业术语搞得一头雾水。简单来说,I2C就像两个人用摩斯密码交流——只需要两根线(SDA数据线和SCL时钟线),就能让主设备(GD32F303)和…...
从零搭建短剧生成AI
当AI遇上短剧创作,会产生怎样的火花?从抖音的1分钟小剧场到YouTube的3分钟微电影,短剧已成为最受欢迎的内容形式之一。而AI,正在让这种创作变得触手可及。AI时代的内容创作革命在数字内容爆炸式增长的时代,短剧以其紧凑…...
终极指南:如何使用Azure Quickstart Templates实现成本管理与预算警报
终极指南:如何使用Azure Quickstart Templates实现成本管理与预算警报 【免费下载链接】azure-quickstart-templates Azure Quickstart Templates 项目地址: https://gitcode.com/gh_mirrors/az/azure-quickstart-templates Azure Quickstart Templates是微软…...
物理网卡down了?虚拟机还能通信吗?看teaming策略就够了
在ESXi虚拟化运维中,物理网卡(vmnic)故障、网线松动、网卡损坏导致网卡down(宕机),是常见的硬件故障场景。很多新手遇到这种情况,会下意识认为所有虚拟机都会断网,但实际并非如此。核…...
抖音无水印视频下载终极指南:5分钟快速掌握免费批量下载技巧
抖音无水印视频下载终极指南:5分钟快速掌握免费批量下载技巧 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback…...
APK安装器完整指南:在Windows上轻松安装安卓应用的终极方案
APK安装器完整指南:在Windows上轻松安装安卓应用的终极方案 【免费下载链接】APK-Installer An Android Application Installer for Windows 项目地址: https://gitcode.com/GitHub_Trending/ap/APK-Installer 你是否想在Windows电脑上直接运行手机应用&…...
Java——Character
Character1、Unicode基础2、检查code point和char3、code point与char的转换4、按code point处理char数组或序列5、字符属性6、字符转换1、Unicode基础 Unicode给世界上每个字符分配了一个编号,编号范围为0x000000~0x10FFFF。编号范围在0x0000ÿ…...
宝塔面板磁盘爆满排查与清理全记录
前言前几天登录宝塔面板,发现磁盘空间告急(日志文件都清理了,怎么磁盘占用率还这么高):81.52G / 98.3G,剩余不足 17%。虽然服务器负载不高,但这个磁盘占用率让人隐隐不安——如果不及时处理&…...
ComfyUI-Impact-Pack深度解析:从AI图像模糊到专业级细节增强的完整解决方案
ComfyUI-Impact-Pack深度解析:从AI图像模糊到专业级细节增强的完整解决方案 【免费下载链接】ComfyUI-Impact-Pack Custom nodes pack for ComfyUI This custom node helps to conveniently enhance images through Detector, Detailer, Upscaler, Pipe, and more. …...
终极Windows激活解决方案:3分钟永久激活Windows和Office的完整指南
终极Windows激活解决方案:3分钟永久激活Windows和Office的完整指南 【免费下载链接】KMS_VL_ALL_AIO Smart Activation Script 项目地址: https://gitcode.com/gh_mirrors/km/KMS_VL_ALL_AIO 你是否曾经遇到过这样的场景:新安装的Windows系统弹出…...
