【数据结构】LRU缓存

LRU缓存

LRU(Least Recently Used,最近最少使用)缓存是一种缓存淘汰策略,用于管理缓存中数据的存储和淘汰。LRU缓存会优先淘汰最近最少使用的数据,以便为新数据腾出空间。它通常用于提高应用程序的性能,通过缓存常用的数据来减少对磁盘或数据库的访问次数。

LRU缓存的基本原理

  • 缓存:LRU缓存通过一个数据结构(通常是字典或散列表)来存储缓存中的数据。数据可以通过键值对的形式存储和访问。
  • 淘汰策略:LRU缓存使用一个有序的数据结构(通常是双向链表)来跟踪数据的使用顺序。数据的插入、访问和删除操作都会更新链表中的数据顺序,以便保持最近最少使用的数据在链表的尾部。
  • 插入操作:当向缓存中插入新数据时,如果缓存已满,则会根据LRU策略删除最近最少使用的数据,然后插入新数据。
  • 访问操作:当访问缓存中的数据时,该数据会被移动到链表的头部,以表示它是最近被使用的数据。
  • 删除操作:当缓存已满且需要插入新数据时,会删除链表尾部的最近最少使用的数据。

LRU缓存的优点

  • 提高性能:LRU缓存通过缓存常用的数据,减少了对慢速存储设备(如磁盘或数据库)的访问次数,从而提高了应用程序的性能。
  • 自适应淘汰:LRU策略根据数据的访问频率和顺序自动调整缓存中的数据,从而保持缓存中的数据始终是最近最常用的数据。

LRU缓存的缺点

  • 内存开销:LRU缓存需要额外的内存来维护数据的有序链表。
  • 复杂性:实现LRU缓存需要维护数据的顺序,并处理数据的插入、访问和删除操作。

C语言中的LRU缓存示例

下面是一个使用C语言实现的LRU缓存示例,展示了基本的插入、访问和删除操作。

首先,定义LRU缓存的数据结构和相关操作:

#include <stdio.h>
#include <stdlib.h>

// 定义缓存的最大大小
#define MAX_SIZE 3

// 双向链表节点
typedef struct Node {
    int key;
    int value;
    struct Node *prev;
    struct Node *next;
} Node;

// LRU缓存结构
typedef struct {
    Node *head;
    Node *tail;
    int size;
    Node *hashTable[MAX_SIZE];  // 哈希表,用于快速查找节点
} LRUCache;

// 初始化LRU缓存
LRUCache *initLRUCache() {
    LRUCache *cache = (LRUCache *)malloc(sizeof(LRUCache));
    cache->head = NULL;
    cache->tail = NULL;
    cache->size = 0;
    for (int i = 0; i < MAX_SIZE; i++) {
        cache->hashTable[i] = NULL;
    }
    return cache;
}

// 释放LRU缓存
void freeLRUCache(LRUCache *cache) {
    Node *current = cache->head;
    while (current != NULL) {
        Node *next = current->next;
        free(current);
        current = next;
    }
    free(cache);
}

// 将节点移动到链表的头部
void moveToHead(LRUCache *cache, Node *node) {
    if (cache->head == node) {
        // 节点已经在头部
        return;
    }
    // 将节点从当前位置移除
    if (node->prev != NULL) {
        node->prev->next = node->next;
    } else {
        cache->head = node->next;
    }
    if (node->next != NULL) {
        node->next->prev = node->prev;
    } else {
        cache->tail = node->prev;
    }
    // 将节点插入到头部
    node->prev = NULL;
    node->next = cache->head;
    if (cache->head != NULL) {
        cache->head->prev = node;
    }
    cache->head = node;
    if (cache->tail == NULL) {
        cache->tail = node;
    }
}

// 插入键值对到缓存中
void put(LRUCache *cache, int key, int value) {
    int hashIndex = key % MAX_SIZE;
    Node *node = cache->hashTable[hashIndex];
    while (node != NULL) {
        if (node->key == key) {
            // 键已存在,更新值并移动到头部
            node->value = value;
            moveToHead(cache, node);
            return;
        }
        node = node->next;
    }
    // 键不存在,创建新节点
    node = (Node *)malloc(sizeof(Node));
    node->key = key;
    node->value = value;
    node->prev = NULL;
    node->next = NULL;
    // 将新节点插入到头部
    moveToHead(cache, node);
    cache->hashTable[hashIndex] = node;
    cache->size++;
    // 如果缓存已满,删除最近最少使用的节点
    if (cache->size > MAX_SIZE) {
        Node *tailNode = cache->tail;
        // 从哈希表中移除
        int tailHashIndex = tailNode->key % MAX_SIZE;
        cache->hashTable[tailHashIndex] = NULL;
        // 从链表中移除
        cache->tail = tailNode->prev;
        if (cache->tail != NULL) {
            cache->tail->next = NULL;
        } else {
            cache->head = NULL;
        }
        free(tailNode);
        cache->size--;
    }
}

// 从缓存中获取值
int get(LRUCache *cache, int key) {
    int hashIndex = key % MAX_SIZE;
    Node *node = cache->hashTable[hashIndex];
    while (node != NULL) {
        if (node->key == key) {
            // 键存在,移动到头部并返回值
            moveToHead(cache, node);
            return node->value;
        }
        node = node->next;
    }
    // 键不存在
    return -1;
}

在上面的代码中,定义了LRU缓存的数据结构 LRUCache,包含一个双向链表(用于跟踪数据的使用顺序)和哈希表(用于快速查找节点)。同时,定义了插入和访问操作。

  • 插入操作:当插入新键值对时,如果键已经存在,则更新值并将节点移动到链表的头部。如果键不存在,则创建新节点并插入到头部。如果缓存已满,则删除链表尾部的节点。
  • 访问操作:当访问缓存中的键时,如果键存在,则将节点移动到链表的头部并返回值;否则返回 -1。

接下来,示例代码展示了如何使用LRU缓存:

int main() {
    // 初始化LRU缓存
    LRUCache *cache = initLRUCache();

    // 插入键值对
    put(cache, 1, 100);
    put(cache, 2, 200);
    put(cache, 3, 300);
    
    // 获取值
    printf("获取键1的值:%d\n", get(cache, 1));  // 输出100
    printf("获取键2的值:%d\n", get(cache, 2));  // 输出200
    printf("获取键3的值:%d\n", get(cache, 3));  // 输出300

    // 插入新键值对,会淘汰最久未使用的键2
    put(cache, 4, 400);

    // 获取值
    printf("获取键1的值:%d\n", get(cache, 1));  // 输出100
    printf("获取键2的值:%d\n", get(cache, 2));  // 输出-1(键2被淘汰)
    printf("获取键3的值:%d\n", get(cache, 3));  // 输出300
    printf("获取键4的值:%d\n", get(cache, 4));  // 输出400

    // 释放LRU缓存
    freeLRUCache(cache);

    return 0;
}

在上面的代码中,我们首先初始化一个LRU缓存,然后插入键值对 (1, 100)(2, 200)(3, 300)。接着,通过调用 get() 函数获取这些键的值。在插入新键值对 (4, 400) 后,最久未使用的键 (2, 200) 被淘汰。因此,再次获取键 2 的值时,将返回 -1

总结

LRU缓存是一种基于双向链表和哈希表的数据结构,用于管理缓存中的数据并自动淘汰最近最少使用的数据。它通过高效的插入、访问和删除操作,提高了应用程序的性能。LRU缓存非常适合用于对数据访问频率较高且具有较强时效性的数据集。

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

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

相关文章

关于豆瓣电影数据抓取以及可视化

首先我们可以先了解以下网络爬虫的定义&#xff1a; 爬虫是一种按照一定的规则&#xff0c;自动地抓取万维网信息的程序或者脚本。它可以在互联网上自动抓取网页内容&#xff0c;将这些信息存储起来。爬虫可以抓取网站的所有网页&#xff0c;从而获取对于我们有价值的信…

BGP的路径属性

路径属性 l每条BGP路由都拥有多个的路径属性&#xff0c;有些是必须携带的&#xff0c;有些是可选添加的 lBGP的路径属性将影响最优路由的选择 lBGP路径属性是描述路由的一组参数&#xff0c;BGP根据路由的属性选择最佳路由&#xff0c;可以人为置值&#xff0c;以便执行路由…

【AI】Deepstream入门(2)Ubuntu20.04安装Deepstream

1、安装GPU驱动 本人显卡型号:RTX4060 Laptop(笔记本专用显卡) 【AI】惠普暗夜精灵9安装Ubuntu20.04+nvidia驱动 2、安装cuda、cuDNN 【AI】Ubuntu20.04安装cuda、cuDNN 3、安装TensorRT 1)下载 下载地址:https://docs.nvidia.com/deeplearning/tensorrt/archives/i…

vue报错:Do not mutate vuex store state outside mutation handlers.

vue报错&#xff1a;Do not mutate vuex store state outside mutation handlers. 原因&#xff1a;在vuex store的state外部直接修改了state的值&#xff0c;但是Vuex要求所有的state的修改必须在vuex中&#xff0c;不允许直接咋组件中修改store中的状态&#xff0c;除非通过m…

FPM 快速报表开发

背景&#xff1a; 使用FPM开发报表时&#xff0c;如果报表字段过多&#xff0c;页面拖拽等操作不方便 报表数量过多时&#xff0c;新建应用操作步骤较为繁琐 更习惯通过少量代码而非页面操作去实现功能 处理&#xff1a; 将FPM报表开发简化为类似GUI端ALV的开发过程:&#xff…

Spring Boot | Spring Boot “自定义“ Redis缓存 “序列化机制“

目录: Spring Boot "自定义" Redis缓存 "序列化机制" &#xff1a;一、基于 "注解" 的 "Redis缓存管理" 的 "默认序列化机制" 和 "自定义序列化机制"1.1 基于 "注解" 的 "Redis缓存管理" 的 …

基于OpenCV的人脸签到系统

效果图 目录文件 camerathread.h 功能实现全写在.h里了 class CameraThread : public QThread {Q_OBJECT public:CameraThread(){//打开序号为0的摄像头m_cap.open(0);if (!m_cap.isOpened()) {qDebug() << "Error: Cannot open camera";}//判断是否有文件,人脸…

Unity 实现原神中的元素反应

一、元素反应 原神中共有七种元素&#xff0c;分别是水、火、冰、岩、风、雷、草。这七种元素能互相作用 Demo下载&#xff1a;Download 元素反应表格图示&#xff0c;可能不够精准 /火水雷冰草岩风绽放原激化火/蒸发超载融化燃烧结晶扩散烈绽放/水蒸发/感电冻结/碎冰绽放结晶…

数据分析:甲基化分析-从DNA methylation的IDAT文件到CpG site的Beta values

介绍 DNA Methylation和疾病的发生发展存在密切相关&#xff0c;它一般通过CH3替换碱基5‘碳的H原子&#xff0c;进而调控基因的转录。常用的DNA methylation是Illumina Infinium methylation arrays&#xff0c;该芯片有450K和850K&#xff08;也即是EPIC&#xff09;。 该脚…

【canvas】前端创造的图片粒子动画效果:HTML5 Canvas 技术详解

前端创造的图片粒子动画效果&#xff1a;HTML5 Canvas 技术详解 我们将深入探讨如何通过 HTML5 的 Canvas 功能&#xff0c;将上传的图片转换成引人入胜的粒子动画效果。这种效果将图片分解成小粒子&#xff0c;并在用户与它们交互时产生动态变化。我们将分步骤详细解析代码&a…

LabVIEW专栏九、类的应用

一、类的应用 接上一章"类" 类在项目中&#xff0c;一般会在类的私有成员簇内&#xff0c;包含一个数据类型为参数类的队列。 例如网口类&#xff0c;里面实际会包含很多信息&#xff0c;有IP地址和端口等等参数。这些参数如果不放在队列引用中缓存下来&#xff0c;…

DevOps(十四)怎么实现Gitlab更新后Jenkins自动发布

目录 1、在 Jenkins 中安装 GitLab 插件 2、在 GitLab 中创建一个访问令牌(Access Token) 3、在 Jenkins 中配置 GitLab 连接 4、在 Jenkins 中创建一个新的任务(Job) 5、在 GitLab 中配置 Webhook 6、以下是一些补充说明和建议 持续集成的一个特点就是开发可以随时提交&…

微服务组件-反向代理(Nginx)

微服务组件-反向代理(Nginx) Nginx 基本概念 1、nginx是什么&#xff1f; ①、Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器同时也提供了IMAP/POP3/SMTP服务。它是一款轻量级的Web服务器/反向代理服务器及电子邮件&#xff08;IMAP/POP3&#xff09;代理服务器&a…

TiDB 6.x 新特性解读 | Collation 规则

对数据库而言&#xff0c;合适的字符集和 collation 规则能够大大提升使用者运维和分析的效率。TiDB 从 v4.0 开始支持新 collation 规则&#xff0c;并于 TiDB 6.0 版本进行了更新。本文将深入解读 Collation 规则在 TiDB 6.0 中的变更和应用。 引 这里的“引”&#xff0c;…

Oracle 监控 SQL 精选 (一)

Oracle数据库的监控通常涉及性能、空间、会话、对象、备份、安全等多个层面。 有效的监控可以帮助 DBA 及时发现和解决问题&#xff0c;提高数据库的稳定性和性能&#xff0c;保障企业的数据安全和业务连续性。 常用的监控指标有&#xff1a; 性能指标&#xff1a; 查询响应时间…

产品推荐 | BittWare基于Altera Agilex“M FPGA的lA-860m加速卡

01 产品概述 BittWare的lA-860m是一款Altera Agilex“M系列FPGA卡&#xff0c;针对吞吐量和内存密集型应用进行了优化。M 系列 FPGA 具有广泛的内存层次结构&#xff0c;包括集成高带宽存储器 &#xff08;HBM2e&#xff09; 和硬内存片上网络 &#xff08;NoC&#xff09;&am…

【QT】ROS2 Humble联合使用QT教程

【QT】ROS2 Humble联合使用QT教程 文章目录 【QT】ROS2 Humble联合使用QT教程1. 安装ROSProjectManager插件2. 创建ROS项目3.一个快速体验的demoReference 环境的具体信息如下&#xff1a; ubunt 22.04ros2 humbleQt Creator 13.0.0ROS ProjectManager 13.0.0 本文建立在已经…

Vivado-IP-DDS and Testbench Learning

DDS内部结构 实现流程 首先新建一个工程&#xff0c;创建bd文件&#xff0c;添加DDS Compiler核&#xff0c;此处不多赘述 Block Design 在观测输出的信号时&#xff0c;需要将最高位符号位的信号取反&#xff0c;这样才能输出正弦波&#xff0c;否则输出的波形如下图所示 将t…

OpenStack云计算(十)——OpenStack虚拟机实例管理,增加一个计算节点并进行实例冷迁移,增加一个计算节点的步骤,实例冷迁移的操作方法

项目实训一 本实训任务对实验环境要求较高&#xff0c;而且过程比较复杂&#xff0c;涉及的步骤非常多&#xff0c;有一定难度&#xff0c;可根据需要选做。可以考虑改为直接观看相关的微课视频 【实训题目】 增加一个计算节点并进行实例冷迁移 【实训目的】 熟悉增加一个…

实验 1--SQL Server2008数据库开发环境

文章目录 实验 1--SQL Server2008数据库开发环境2.4.1 实验目的2.4.2 实验准备2.4.3 实验内容1.利用 SSMS 访问系统自带的Report Server 数据库。2.熟悉了解 SMSS对象资源管理器树形菜单相关选择项的功能。(1)右键单击数据库Report Server&#xff0c;查看并使用相关功能;(2)选…
最新文章