LRU缓存 数据结构设计(C++)
做LeetCode第146题LRU缓存,觉得收获不小,特此记录。
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类:
- LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存。
- int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
- void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该逐出最久未使用的关键字。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。
关于LRU的规则,学过操作系统的肯定都知道,此处不再赘述。
题目关键在于 get() 和 put() 必须O(1),这就使得我们选择数据结构的时候需要有些考究。
首先我们回顾一下本科学过的数据结构知识,存储这样的多个key-value对肯定是使用线性表存储,而线性表又分为顺序表(数组)和链表,由于题目中涉及到了插入和删除(逐出),且有O(1)的条件限制,所以肯定是使用链表来存储。
确定了基本数据结构,我们再来想想如何使用或改造使其满足题目需要。
首先考虑 get(),我们需要做到如果关键字key在缓存中,就能查找到关键字的值,且有O(1)的条件限制,那自然是使用哈希表,哈希表的键就是LRU缓存中的key自然不用说,关键在于 哈希表的值是什么?直观想法那就是LRU的value呗,其实不然,因为我们其实不仅要通过key查到value,还有一个隐含的操作,即需要把我们刚刚查过的这个LRU缓存节点置于最高优先级的位置上(LRU的规则),所以该哈希表的value应该是一个链表节点,我们通过key查到这个链表节点以后,就可以对其进行操作以改变其优先级,同时我们也可以直接通过node->value来取得key对应的value值,一举两得。
那到底怎么体现优先级,怎么做到关键字的插入和逐出呢,其实很简单,在链表头部的表示优先级最高(即最近使用),在链表尾部的表示优先级最低(即最久未使用),这样一来 put() 的编写就容易了,当来一个新关键字的时候,就把其节点插入链表头,同时更新哈希表。每当链表中的节点个数超过LRU的容量时,我们就删除掉链表尾的的元素,同时更新哈希表。
现在我们还有一个问题需要解决,就是使用普通的单链表,是否能满足put()和get()均为O(1)的题目要求。根据以上的分析,我们对链表会有以下操作:
- 把一个新节点插入链表头。当我们put()一个新关键字,其不存在于缓存中,且缓存没有满时,就把这个新的关键字节点插入链表头。
- 把一个链表中的节点移动到链表头。当我们get()一个关键字,其存在于缓存中,我们就需要把这个节点从当前位置插入到链表头部。
- 删除链表尾的节点。当我们put()一个新关键字,其不存在于缓存中,我们需要插入新节点,但此时缓存满了,我们需要把最近最少使用的节点从链表中剔除掉,也就是删除链表尾的节点。
根据以上对链表的要求,可以确定只有双链表能满足要求,单链表、单循环链表、带尾指针的单循环链表都无法在O(1)的时间下做到以上三个操作,大家可以模拟一下。
确定好数据结构,代码就很好写了,只需按需实现LRU功能即可:
struct doubleLinkNode {int key, value;doubleLinkNode *pre, *next;
};class LRUCache {private:doubleLinkNode *head, *rear;int size, capacity;unordered_map<int, doubleLinkNode*> map;void insert(doubleLinkNode* node) {// 把一个非链表上的节点插入到表头node->pre = head;node->next = head->next;head->next->pre = node;head->next = node;}void insertHead(doubleLinkNode* node) {// 把一个链表上的节点插入到表头node->pre->next = node->next;node->next->pre = node->pre;insert(node);}int deleteRear() {doubleLinkNode *node = rear->pre;node->pre->next = node->next;node->next->pre = node->pre;int key = node->key;delete node;return key;}public:LRUCache(int cap): size(0), capacity(cap) {head = new doubleLinkNode();rear = new doubleLinkNode();head->next = rear;head->pre = nullptr;rear->next = nullptr;rear->pre = head;}int get(int key) {if(map.count(key)) {insertHead(map[key]);return map[key]->value;}return -1;}void put(int key, int value) {if(map.count(key)) {doubleLinkNode *node = map[key];if(node->value != value) {node->value = value;}insertHead(node);} else {doubleLinkNode *node = new doubleLinkNode;node->key = key;node->value = value;insert(node);++size;if(size > capacity) {int nodeKey = deleteRear();map.erase(nodeKey);--size;}map[key] = node;}}
};
LRU缓存 数据结构设计(C++)相关推荐
- 六、数据结构设计-栈与队列
学习来源: 代码随香炉:https://www.programmercarl.com/ labuladong算法:https://labuladong.github.io/algo/ 数据结构设计+栈 ...
- LeetCode实战:LRU缓存机制
背景 为什么你要加入一个技术团队? 如何加入 LSGO 软件技术团队? 我是如何组织"算法刻意练习活动"的? 为什么要求团队的学生们写技术Blog 题目英文 Design and ...
- 看动画轻松理解「链表」实现「LRU缓存淘汰算法」
作者 | 程序员小吴,哈工大学渣,目前正在学算法,开源项目 「 LeetCodeAnimation 」5500star,GitHub Trending 榜连续一月第一. 本文为 AI科技大本营投稿文章 ...
- 算法必知 --- LRU缓存淘汰算法
作者:_code_x 链接:https://www.jianshu.com/p/b7fed77324b9 写在前 就是一种缓存淘汰策略. 计算机的缓存容量有限,如果缓存满了就要删除一些内容,给新内容腾 ...
- LeetCode第 146 号问题: LRU 缓存机制
题目描述 运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制.它应该支持以下操作: 获取数据 get 和 写入数据 put . 获取数据 get(key) - 如果密钥 (ke ...
- 10 行 Java 代码实现 LRU 缓存
10 行 Java 代码实现 LRU 缓存 (整理自网络) 最近最少使用缓存的回收 为了实现缓存回收,我们需要很容易做到: 查询出最近最晚使用的项 给最近最少使用的项做一个标记 链表可以实现这两个操作 ...
- 数据结构与算法 / LRU 缓存淘汰算法
一.诞生原因 缓存是一种提供数据读取性能的技术,在硬件设计.软件开发中有广泛的应用,比如常见的 CPU 缓存,DB 缓存和浏览器缓存等.但是缓存的大小是有限的,需要一定的机制判断哪些数据需要淘汰,即: ...
- C#简单实现LRU缓存
最近跟同学吃饭扯淡的时候,由技术扯到薪资,又由薪资扯到他找工作时跟面试官是怎么扯淡拿高工资的,各种技术一顿侃,总之只要啥都了解就没问题了.谈到缓存的时候,我试探性的问了问- -你还记得LRU怎么写吗, ...
- 实现 LRU 缓存机制
实现 LRU 缓存机制 文章目录 实现 LRU 缓存机制 一.什么是 LRU 算法 二.LRU 算法描述 三.LRU 算法设计 四.代码实现 一.什么是 LRU 算法 LRU 就是一种缓存淘汰策略.( ...
最新文章
- python学习笔记(二)——散列类型(字典、集合)
- Windows Phone中Wallet钱包的使用
- python实现一个三级菜单
- 【HDU - 4217 】Data Structure? (线段树求第k小数)
- 这个阿里程序员,干了件很轴的事儿
- C语言 野指针 - C语言零基础入门教程
- Oracle遇到的一些坑
- 绝对不能错过!计算机视觉Polygon Mesh Processing读书笔记——4微分几何中的曲线
- 谷歌:中国版搜索引擎之心不死!
- mysql 变量定义和赋值_MySQL变量解析
- 在WebForm戏说 js的注释
- 20. DICOM图像层级分类-DCMTK-层级分类
- 用计算机视觉可以做什么项目,程序员必看:13个GitHub开源又炫酷的计算机视觉项目...
- 软件项目文档及其必要性
- 数学物理计算机的思考
- 如何寻找logo创意灵感?推荐这8个设计灵感网站
- 百度网址安全中心提醒:该页面可能已被非法篡改!如何去解决?
- jQuery 上拉加载更多
- windows驱动开发教程 滴水_滴水编程达人全套
- 王家林人工智能AI第14课:通过K-Nearest Neighbors改进在Social Network上销售汽车推荐系统的精准性 老师微信13928463918
热门文章
- 自定义控件_VIewPager显示多个Item
- !+\v1 用来“判断浏览器类型”还是用来“IE判断版本”的问题!
- How to check bad fix
- echo 12345678 | base64 产生的结果跟12345678真正的base64编码不对
- linux 7 services设定,CENTOS/RHEL7系统中设置SYSTEMD SERVICE的ULIMIT资源限制
- mysql for mac中文_mysql for Mac 下创建数据表中文显示为?的解决方法
- linux java 安装配置_类Linux环境安装jdk1.8及环境变量配置详解
- php数据库postgresql,PHP 操作 PostgreSQL数据库
- 热敏电阻温度特性曲线_热敏电阻与体温计的应用关系
- python不能创建字典的是_用Python创建带有重复键的字典