LRU的实现

2023-11-11

题目内容

实现一个 LRUCache 类,三个接口:

  • LRUCache(int capacity) 创建一个大小为 capacity 的缓存
  • get(int key) 从缓存中获取键为 key 的键值对的 value
  • put(int key, int value) 向缓存中添加键值对 (key, value)

要求 getput 的均摊时间复杂度为 O ( 1 ) O(1) O(1)

题解

对于 get 操作,我们需要快速获取到 key 对应的键值对,哈希表可以解决。
对于 put 操作,我们需要快速 put 一个键值对,也可以用哈希表解决。

但是问题在于,我们 getput 时,需要维护键值对最近使用的情况。

这部分我们可以用双向链表维护,每次操作一个键值对时,将其从原来链表的位置中移除,重新添加到链表头。定义链表头的数据是最近一次使用的,链表尾是最近最少使用的。

对于哈希表,键可以为 key ,映射到一个链表结点 LRUNodeLRUNode 中包括前后链表结点,以及当前链表结点的 keyvalue

为什么我们要在链表结点中存储 key 呢,直接看上去没什么用。
考虑我们需要利用 LRU 策略从缓存中弹出一个最近最少使用的结点。
根据我们的定义,链表尾的结点是最近最少使用的,除了要将其从链表中移除,还需要将其从哈希表中移除,而从哈希表中移除需要使用 key ,这才是链表结点中存储 key 的原因。

定义LRU中的链表结点 LRUNode

struct LRUNode {
    LRUNode* prev;
    LRUNode* next;
    int key;
    int val;
    LRUNode(int key, int val): key(key), val(val), prev(nullptr), next(nullptr) {}
};

对于 LRUNode ,其会从链表中被移除,也会被添加到链表,所以需要实现这两个方法

void removeLRUNodeFromLinklist(LRUNode* node) {
	node->prev->next = node->next;
    node->next->prev = node->prev;
}

void insertLRUNodeToLinklist(LRUNode* node) {
	node->next = head->next;
	head->next->prev = node;
	head->next = node;
	node->prev = head;
}

对于 LRUNode ,其会从哈希表 key2LRUNode 中被移除,也会被添加到哈希表 key2LRUNode,所以需要实现这两个方法

void removeLRUNodeFromHashTable(LRUNode* node) {
	if (!key2LRUNode.count(node->key)) return;
	key2LRUNode.erase(node->key);
}

void insertLRUNodeToHashTable(LRUNode* node) {
	key2LRUNode[node->key] = node;
}

接下来实现 get 的逻辑

int get(int key) {
	// key 不存在
	if (!key2LRUNode.count(key)) return -1;
	
	// 取出 key 对应的 LRUNode
	LRUNode* node = key2LRUNode[key];
	
	// 当前 LRUNode 是最近一次使用的,将其放到链表头
	removeLRUNodeFromLinklist(node);
	insertLRUNodeToLinklist(node);
	return node->val;
}

继续实现 put 的逻辑

void put(int key, int value) {
	// 如果不存在 key ,则需要新建该键值对
    if (!key2LRUNode.count(key)) {
    	// 缓存已满,要从缓存中通过LRU策略弹出最近最少使用的LRUNode
        if (size_ >= capacity_) {
        	// 链表尾即最近最少使用的
            LRUNode* node = tail->prev;
            // 从链表中删去
            removeLRUNodeFromLinklist(node);
            // 从哈希表中删去
            removeLRUNodeFromHashTable(node);
            // 释放 node 的内存空间,如果是智能指针就不需要手动释放了
            delete node;
            // 释放一个空间
            size_ -= 1;
        }
        // 创建一个新的 LRUNode
        LRUNode* newLRUNode = new LRUNode(key, value);
        // 添加到链表中
        insertLRUNodeToLinklist(newLRUNode);
        // 添加到哈希表中
        insertLRUNodeToHashTable(newLRUNode);
        size_ += 1;

    } else {
    	// 获取到 key 对应的已存在于缓存中的 LRUNode 节点
        LRUNode* node = key2LRUNode[key];
        // 更新键值对的权值
        node->val = value;
        // 从链表中删去
		removeLRUNodeFromLinklist(node);
		// 添加到链表中
		insertLRUNodeToLinklist(node);
        // 添加到哈希表中,其实这步是不需要的,因为哈希表对应的是 LRUNode 的地址
        insertLRUNodeToHashTable(node);
        // 这里只是 key 对应的 value 修改了,size_ 不变
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

LRU的实现 的相关文章

  • Java实现LRU

    首先看看什么是LRU LRU是Least Recently Used的缩写 xff0c 即最近最少使用 xff0c 是一种常用的页面置换算法 xff0c 选择最近最久未使用的页面予以淘汰 该算法赋予每个页面一个访问字段 xff0c 用来记录
  • 虚拟内存和LRU页面置换算法

    虚拟内存 1 虚拟内存的基本概念 传统存储管理方式的特征 传统的内存管理策略都是为了同时将多个进程保存进内存中 xff0c 它们具有以下的共同特征 xff1a 一次性 作业必须一次性全部装入内存后 xff0c 才能开始运行 xff08 静态
  • 【每日一题】ABC194E-Mex Min

    题目内容 原题链接 给定一个长度为 n n n 的整数数组 a a a 求所有长度为 m m
  • LeetCode 460. LFU Cache

    原题网址 https leetcode com problems lfu cache Design and implement a data structure for Least Frequently Used LFU cache It
  • PTA 剥洋葱(C语言 + 详细注释 + 代码超简单)

    输入格式 一行 一个整数 即图形的层数 输出格式 如上述图形 输入样例 3 输出样例 AAAAA ABBBA ABCBA ABBBA AAAAA 打印图形题关键是找规律 一般只需两重循环 行循环 列循环 include
  • 国王将金币作为工资,发放给忠诚的骑士。 问题 G: 金币

    题目描述 国王将金币作为工资 发放给忠诚的骑士 第一天 骑士收到一枚金币 之后两天 第二天和第三天 每天收到两枚金币 之后三天 第四 五 六天 每天收到三枚金币 之后四天 第七 八 九 十天 每天收到四枚金币 这种工资发放模式会一直这样延续
  • 【每日一题】ARC137B - Count 1’s

    题目内容 原题链接 给定一个长度为 n n n 的整数数组 a a a a i
  • ◆考试题目◆◇NOIP模拟赛◇turtle(乌龟)

    NOIP模拟赛 turtle Description 一只乌龟由于智商低下 它只会向左或向右走 不过它会遵循主人小h的指令 F 向前走一步 T 掉头 现在小h给出一串指令 由于小h有高超的计算能力 他可以马上知道乌龟最后走到哪里 为了难倒小
  • 关于 Python 之 Matplotlib 的总结

    文章目录 通用 简单例子 中文显示问题 参数 颜色 color参数表 线型 linestyle ls 参数表 标记符号 marker参数表 位置 legend loc参数表 plt 常用命令 图形模板 柱形图 饼图 散点图 直方图 箱线图
  • 【缓存算法】LRU 最近最少使用

    LRU是Least Recently Used 最近最少使用 LRU缓存就是使用这种原理实现 简单的说就是缓存一定量的数据 当超过设定的阈值时就把一些过期的数据删除掉 LRU思想 固定缓存大小 需要给缓存分配一个固定的大小 每次读取缓存都会
  • 【算法】树状数组维护总结

    本文仅对树状数组的使用作一个总结 并非讲解 这里的操作都对长度为 n n n 的数组 a a a 进行操作 单点修改 区间查询 暴力做法 修改
  • LRU的实现

    题目内容 实现一个 LRUCache 类 三个接口 LRUCache int capacity 创建一个大小为 capacity 的缓存 get int key 从缓存中获取键为 key 的键值对的 value put int key in
  • 初步认识Ehcache清空缓存的3种策略

    Ehcache是一种广泛使用的开源Java分布式缓存 主要面向通用缓存 Java EE和轻量级容器 它具有内存和磁盘存储 缓存加载器 缓存扩展 缓存异常处理程序 一个gzip缓存servlet过滤器 支持REST和SOAP api等特点 在
  • LRU算法的Java实现

    一 LRU算法介绍 LRU算法全称Least Recently Used 也就是检查最近最少使用的数据的算法 这个算法通常使用在内存淘汰策略中 用于将不常用的数据转移出内存 将空间腾给最近更常用的 热点数据 算法很简单 只需要将所有数据按使
  • LRU LFU 概念、底层原理及其实现 超详细~

    0 前置提要 本篇约为8650字 阅读完需要约40 60分钟 主要介绍页面置换算法 LRU和LFU的原理及其实现 对应leetcode140和460 如果能给个赞就更好了 1 从内存置换算法说起 计算机的运行的程序和数据保存在内存中 内存的
  • 【算法竞赛】Python快速入门指南

    该指南由GPT4编写 用于快速入门蓝桥杯Python组 当然 仅限入门而已 本指南由GPT 4生成 我只是负责引导 并对内容进行整理和补充 一直以来我都是使用C 作为算法竞赛语言 但是奈何C 组太卷 自己又太菜 于是另谋他路 Prompt模
  • 清除Python中所有lru_cache

    我在 python 中有一些带有 lru cache 缓存的函数 例如 lru cache maxsize None def my function 虽然我可以单独清除缓存 例如my function cache clear 有没有办法一次
  • Python:构建 LRU 缓存

    我身边有6 00 000 entries in MongoDB采用以下格式 feature category count where feature可以是任何词 category为正或负 并且 count告诉某个功能在该类别的文档中出现了多
  • 记住一个函数,以便当我在 Python 中重新运行该文件时它不会被重置

    我经常在 Python 中进行交互式工作 其中涉及一些我不想经常重复的昂贵操作 我通常运行我经常处理的任何 Python 文件 如果我写 import functools32 functools32 lru cache def square
  • Android LruCache(Android 3.1)线程安全

    是新的Android类LruCache http developer android com reference android util LruCache html线程安全 java 文档说 这个类是线程安全的 通过在缓存上同步以原子方式

随机推荐