做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)的题目要求。根据以上的分析,我们对链表会有以下操作:

  1. 把一个新节点插入链表头。当我们put()一个新关键字,其不存在于缓存中,且缓存没有满时,就把这个新的关键字节点插入链表头。
  2. 把一个链表中的节点移动到链表头。当我们get()一个关键字,其存在于缓存中,我们就需要把这个节点从当前位置插入到链表头部。
  3. 删除链表尾的节点。当我们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++)相关推荐

  1. 六、数据结构设计-栈与队列

    学习来源: 代码随香炉:https://www.programmercarl.com/ labuladong算法:https://labuladong.github.io/algo/ 数据结构设计+栈 ...

  2. LeetCode实战:LRU缓存机制

    背景 为什么你要加入一个技术团队? 如何加入 LSGO 软件技术团队? 我是如何组织"算法刻意练习活动"的? 为什么要求团队的学生们写技术Blog 题目英文 Design and ...

  3. 看动画轻松理解「链表」实现「LRU缓存淘汰算法」

    作者 | 程序员小吴,哈工大学渣,目前正在学算法,开源项目 「 LeetCodeAnimation 」5500star,GitHub Trending 榜连续一月第一. 本文为 AI科技大本营投稿文章 ...

  4. 算法必知 --- LRU缓存淘汰算法

    作者:_code_x 链接:https://www.jianshu.com/p/b7fed77324b9 写在前 就是一种缓存淘汰策略. 计算机的缓存容量有限,如果缓存满了就要删除一些内容,给新内容腾 ...

  5. LeetCode第 146 号问题: LRU 缓存机制

    题目描述 运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制.它应该支持以下操作: 获取数据 get 和 写入数据 put . 获取数据 get(key) - 如果密钥 (ke ...

  6. 10 行 Java 代码实现 LRU 缓存

    10 行 Java 代码实现 LRU 缓存 (整理自网络) 最近最少使用缓存的回收 为了实现缓存回收,我们需要很容易做到: 查询出最近最晚使用的项 给最近最少使用的项做一个标记 链表可以实现这两个操作 ...

  7. 数据结构与算法 / LRU 缓存淘汰算法

    一.诞生原因 缓存是一种提供数据读取性能的技术,在硬件设计.软件开发中有广泛的应用,比如常见的 CPU 缓存,DB 缓存和浏览器缓存等.但是缓存的大小是有限的,需要一定的机制判断哪些数据需要淘汰,即: ...

  8. C#简单实现LRU缓存

    最近跟同学吃饭扯淡的时候,由技术扯到薪资,又由薪资扯到他找工作时跟面试官是怎么扯淡拿高工资的,各种技术一顿侃,总之只要啥都了解就没问题了.谈到缓存的时候,我试探性的问了问- -你还记得LRU怎么写吗, ...

  9. 实现 LRU 缓存机制

    实现 LRU 缓存机制 文章目录 实现 LRU 缓存机制 一.什么是 LRU 算法 二.LRU 算法描述 三.LRU 算法设计 四.代码实现 一.什么是 LRU 算法 LRU 就是一种缓存淘汰策略.( ...

最新文章

  1. python学习笔记(二)——散列类型(字典、集合)
  2. Windows Phone中Wallet钱包的使用
  3. python实现一个三级菜单
  4. 【HDU - 4217 】Data Structure? (线段树求第k小数)
  5. 这个阿里程序员,干了件很轴的事儿
  6. C语言 野指针 - C语言零基础入门教程
  7. Oracle遇到的一些坑
  8. 绝对不能错过!计算机视觉Polygon Mesh Processing读书笔记——4微分几何中的曲线
  9. 谷歌:中国版搜索引擎之心不死!
  10. mysql 变量定义和赋值_MySQL变量解析
  11. 在WebForm戏说 js的注释
  12. 20. DICOM图像层级分类-DCMTK-层级分类
  13. 用计算机视觉可以做什么项目,程序员必看:13个GitHub开源又炫酷的计算机视觉项目...
  14. 软件项目文档及其必要性
  15. 数学物理计算机的思考
  16. 如何寻找logo创意灵感?推荐这8个设计灵感网站
  17. 百度网址安全中心提醒:该页面可能已被非法篡改!如何去解决?
  18. jQuery 上拉加载更多
  19. windows驱动开发教程 滴水_滴水编程达人全套
  20. 王家林人工智能AI第14课:通过K-Nearest Neighbors改进在Social Network上销售汽车推荐系统的精准性 老师微信13928463918

热门文章

  1. 自定义控件_VIewPager显示多个Item
  2. !+\v1 用来“判断浏览器类型”还是用来“IE判断版本”的问题!
  3. How to check bad fix
  4. echo 12345678 | base64 产生的结果跟12345678真正的base64编码不对
  5. linux 7 services设定,CENTOS/RHEL7系统中设置SYSTEMD SERVICE的ULIMIT资源限制
  6. mysql for mac中文_mysql for Mac 下创建数据表中文显示为?的解决方法
  7. linux java 安装配置_类Linux环境安装jdk1.8及环境变量配置详解
  8. php数据库postgresql,PHP 操作 PostgreSQL数据库
  9. 热敏电阻温度特性曲线_热敏电阻与体温计的应用关系
  10. python不能创建字典的是_用Python创建带有重复键的字典