关于LRU缓存简单记录以及代码补全。

目录

  • 大概思路
  • 时间空间复杂度分析
  • 指针操作具体细节
    • 代码
    • 双向链表设计
    • 私有成员变量设计:
    • 构造函数和析构函数设计:
    • get与put具体设计
    • 双向指针的具体细节
      • 添加到头节点函数
      • 删除尾节点函数
      • 删除节点函数
      • 删除节点函数
    • 感想

今天面试考到LRU,太紧张了,完全傻了。。。赶紧做个记录

LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。

LRU 缓存:
设计和构建一个“最近最少使用”缓存,该缓存会删除最近最少使用的项目。缓存应该从键映射到值(允许你插入和检索特定键对应的值),并在初始化时指定最大容量。当缓存被填满时,它应该删除最近最少使用的项目。

它应该支持以下操作: 获取数据 get 和 写入数据 put 。

获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。

示例:

LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );
cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // 返回 1
cache.put(3, 3); // 该操作会使得密钥 2 作废
cache.get(2); // 返回 -1 (未找到)
cache.put(4, 4); // 该操作会使得密钥 1 作废
cache.get(1); // 返回 -1 (未找到)
cache.get(3); // 返回 3
cache.get(4); // 返回 4

大概思路

这一题面试官说用到双向链表和哈希map。
双向链表按照被使用的顺序存储了这些键值对,靠近头部的键值对是最近使用的,而靠近尾部的键值对是最久未使用的。
哈希表,通过缓存数据的键映射到其在双向链表中的位置。
注意双向链表中存储的元素是key, value;
哈希表中存储的是key和双向链表节点。

太紧张了,导致一开始都不知道map的value具体含义,还以为是频率计数用的。。。其实并无特殊含义,仅仅是一个值。
get操作:
1、判断key是否存在,不存在返回-1
2、如果存在,返回该节点值并且将对应的节点移动到双向链表的头部。
put操作:
1、判断key是否存在
2、key不存在,使用key和value创建一个新的节点,然后在双向链表头部插入这个键值对。
3、将 key 和该节点添加进哈希表中。
4、判断双向链表的节点数是否超出容量,如果超出容量,则删除双向链表的尾部节点,并删除哈希表中对应的项;
5、key存在。先通过哈希表定位,再将对应的节点的值更新为 value,并将该节点移到双向链表的头部。

时间空间复杂度分析

访问哈希表的时间复杂度为 O(1)
在双向链表头和尾部增减节点时间复杂度也是O(1)

指针操作具体细节

将一个节点移动到头部可以分为:
1、删除该节点
2、在头节点前插入一个相同的节点

还有就是虚拟头节点和虚拟尾节点是使用:
添加节点和删除节点的时候就就比较方便了,这个在单向链表的设计中也有涉及:
707. 设计链表
下面给出具体的代码:

代码

双向链表设计

关于双向链表节点的定义

class DLinkedNode {//存储键、值int key, value;//定义两个指针,一个指向前一个节点,一个指向下一个节点DLinkedNode* prev;DLinkedNode* next;//构造函数DLinkedNode(): key(0), value(0), prev(nullptr), next(nullptr) {}DLinkedNode(int _key, int _value): key(_key), value(_value), prev(nullptr), next(nullptr) {}
};

关于LRUCache类的设计:

私有成员变量设计:

private://哈希表unordered_map<int, DLinkedNode*> cache;//虚拟头指针和虚拟尾指针DLinkedNode* head;DLinkedNode* tail;//当前LRUCache中元素(键值对)个数int size;//LRUCache最多能容纳多少键值对int capacity;

构造函数和析构函数设计:

构造函数:
这里给出最大容量,我们还需要初始化size的大小和主动添加虚拟头指针和虚拟尾指针,并将两者链接起来

    LRUCache(int capacity) {_capacity = capacity;_size = 0;dummyhead = new DLinkedNode();dummytail = new DLinkedNode();dummyhead->next = dummytail;dummytail->prev = dummyhead;}

析构函数:
主要是将两个虚拟头节点和虚拟尾节点释放掉

~LRUCache() {delete head;delete tail;}

get与put具体设计

get函数:

int get(int key)
{//如果找不到,返回-1if(chache.find(key) == chache.end()){return -1;}//如果key存在//哈希定位DLinkedNode* node = chache[key];//将该节点添加到双向链表头部moveHead(node);//返回该节点值return node->value;
}

put函数:

void put(int key, int value)
{//如果key不存在,创建一个新节点if(cache.find(key) == cache.end()){//如果预计超出容量,删除双向链表尾部节点if(size + 1 > capacity){//删除双向链表尾部节点DLinkedNode* deleted_node = deleteTail();//删除哈希表中对应部分cache.erase(deleted_node->key);delete deleted_node;size--;}DLinkedNode* node = new DLinkedNode(key,value);chache[key] = node;//将该节点添加到双向链表头部addHead(node);size++;}//如果key存在,通过哈希定位,修改value,移动到头部else{DLinkedNode* node = chache[key];node->value = value;moveHead}
}

双向指针的具体细节

添加到头节点函数

void addHead(DLinkedNode* node)
{//双向指针操作 + 虚拟头节点node->prev = head;node->next = head->next;head->next->prev = node;head->next = node;
}

删除尾节点函数

DLinkedNode* deleteTail()
{//尾节点是虚拟tail前面一个。DLinkedNode* node = tail->prev;deleteNode(node);return node;
}

删除节点函数

指针绕来绕去。。。面试没写出来。。。

void deleteNode(DLinkedNode* node)
{node->prev->next = node->next;node->next->prev = node->prev;
}

删除节点函数

void moveHead(DLinkedNode* node)
{//先删除当前节点deleteNode(node);//然后将它加入头部addHead(node);
}

感想

第一次面试,还是太紧张了,说话都说不利索。
关键还是指针链表操作的繁琐。。。没想到面试会考这题。。
这一题和实际应用关联较大,而且是设计题,比较考研综合能力。
构造函数和双向链表的定义都得自己写,双向链表我平时用到的不是很多,还得多加练习。
为了面试,晚饭都没吃。。。
面试官声音好听,态度也很温和,像个邻家大哥哥,面完我还要再去面下一个,一个人一个小时,感觉也挺累的。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

码农干货系列【4】--图像识别之矩形区域搜索

简介 定位某个图片的矩形区域是非常有用的&#xff0c;这个可以通过手动的选择某个区域来实现定位&#xff0c;图片相关的软件都提供了这个功能&#xff1b;也可以像本篇一个通过程序来实现智能定位。前者会有误差&#xff0c;效率低下&#xff1b;后者选区精度高&#xff0c;效…

算法题复习(回溯)

目录base code棋盘问题51. N 皇后37. 解数独组合问题77. 组合未剪枝优化剪枝优化216. 组合总和 III未剪枝优化剪枝优化17. 电话号码的字母组合39. 组合总和未剪枝优化剪枝优化40. 组合总和 II,挺重要的&#xff0c;涉及到去重了切割问题131. 分割回文串子集问题78. 子集90. 子集…

pfa是什么意思_PFA的完整形式是什么?

pfa是什么意思PFA&#xff1a;预测性故障分析 (PFA: Predictive Failure Analysis) PFA is an abbreviation of Predictive Failure Analysis. It is a technique of a mechanism of the computer that is used to predict impending failures of software or hardware compone…

SqlServer视图(view)

--上节回顾--1.什么是事务--2.事务的特征--原子性、一致性、隔离性、持久性--3.与事务相关的t-sql命令--开启事务&#xff1a;begin transaction--提交事务&#xff1a;commit transaction--回滚事务&#xff1a;rollback transaction ----------------视图-------------------…

Android中的广播Broadcast详解

今天来看一下Android中的广播机制&#xff0c;我们知道广播Broadcast是Android中的四大组件之一&#xff0c;可见他的重要性了&#xff0c;当然它的用途也很大的&#xff0c;比如一些系统的广播&#xff1a;电量低、开机、锁屏等一些操作都会发送一个广播&#xff0c;具体的And…

sql check约束_在SQL中使用CHECK约束

sql check约束Basically, CHECK constraint is used to LIMIT in columns for the range of values. We can use this constraint for single or multiple columns. 基本上&#xff0c; CHECK约束用于限制值范围内的列 。 我们可以将此约束用于单列或多列。 In single column,…

.NET线程池

摘要 深度探索 Microsoft .NET提供的线程池&#xff0c; 揭示什么情况下你需要用线程池以及 .NET框架下的线程池是如何实现的&#xff0c;并告诉你如何去使用线程池。 内容 介绍 .NET中的线程池 线程池中执行的函数 使用定时器 同步对象的执行 异步I/O操作 监视线程池 死锁 有关…

折线分割平面

Input输入数据的第一行是一个整数C,表示测试实例的个数&#xff0c;然后是C 行数据&#xff0c;每行包含一个整数n(0<n<10000),表示折线的数量。Output对于每个测试实例&#xff0c;请输出平面的最大分割数&#xff0c;每个实例的输出占一行。Sample Input2 1 2Sample Ou…

《c++特性》

目录多态构造函数和析构函数存在多态吗&#xff1f;虚函数表虚析构函数纯虚函数和抽象类运行时多态和编译时多态的区别继承设计实例指针对象和普通对象的区别正确初始化派生类方式继承和赋值的兼容规则protected 和 private 继承基类与派生类的指针强制转换如何用C实现C的三大特…

Scala中的while循环

在Scala中的while循环 (while loop in Scala) while loop in Scala is used to run a block of code multiple numbers of time. The number of executions is defined by an entry condition. If this condition is TRUE the code will run otherwise it will not run. Scala中…

Linux操作系统启动过程

在做开发的过程中&#xff0c;突然发现&#xff0c;要对系统做一些有意义的改变&#xff0c;必须要对操作系统的启动过程有一定的了解&#xff0c;不然就是修改你都不知道从哪里下手啊&#xff0c;然后就是找来资料看&#xff0c;去网上看别人的博客&#xff0c;有了前一周一些…

方法命名的区别

GetDecimalFromString ExtractDecimal 这2个方法名那个比较好呢。上边的明显的是中式英语&#xff0c;单词拼凑而成的。下边的更加流畅一些。方法名称取名还是很有要求的。要通俗易懂还要符合文法。从上边的可以扩展出什么想法呢。 ExtractDecimalExtractDoubleExtractInt16Ext…

工作排序问题

Problem statement: 问题陈述&#xff1a; Given an array of jobs where every job has a deadline and a profit. Profit can be earned only if the job is finished before the deadline. It is also given that every job takes a single unit of time, so the minimum p…

牛客网与leetcode刷题(高频题中简单or中等的)

目录1、反转链表2、排序3、先序中序后序遍历4、最小的k个数5、子数组的最大累加和6、 用两个栈实现队列7、142. 环形链表 II8、20. 有效的括号9、最长公共子串(动态规划),磕磕绊绊10、二叉树之字形层序遍历11、重建二叉树12、LRU缓存13、合并两个有序链表15、大数加法16、一个二…

AMUL的完整形式是什么?

AMUL&#xff1a;阿南德牛奶联盟有限公司 (AMUL: Anand Milk Union Limited) AMUL is an abbreviation of Anand Milk Union Limited. It is an Indian milk product cooperative dairy organization that is based in the small town of Anand in the state of Gujarat. AMUL …

mochiweb 源码阅读(十一)

大家好&#xff0c;今天周六&#xff0c;继续接着上一篇&#xff0c;跟大家分享mochiweb源码。上一篇&#xff0c;最后我们看到了mochiweb_socket_server:listen/3函数&#xff1a; listen(Port, Opts, State#mochiweb_socket_server{sslSsl, ssl_optsSslOpts}) ->case moch…

Android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能 (转)

转载请注明出处&#xff1a;http://blog.csdn.net/guolin_blog/article/details/9255575 最 近项目中需要用到ListView下拉刷新的功能&#xff0c;一开始想图省事&#xff0c;在网上直接找一个现成的&#xff0c;可是尝试了网上多个版本的下拉刷新之后发现效果都不怎么理 想。有…

Python中的append()和extend()

列出append()方法 (List append() method) append() method is used to insert an element or a list object to the list and length of the List increased by the 1. append()方法用于将元素或列表对象插入列表&#xff0c;并且列表长度增加1。 Syntax: 句法&#xff1a; …

红黑树的实现

目录1、红黑树原理1、红黑树性质2、变换规则&#xff08;从插入结点的角度来讲&#xff09;1.变色2.左旋3.右旋3、删除结点需要注意的地方2、代码1、定义结点以及构造函数2、定义红黑树类以及声明它的方法3、左旋4、右旋5、插入操作6、修正操作7、删除操作3、参考链接1、红黑树…

118 - ZOJ Monthly, July 2012

http://acm.zju.edu.cn/onlinejudge/showContestProblems.do?contestId339 都是赛后做的。。。弱爆了 A题是找由2和5组成的数字的个数 直接打个表就行了 只是比赛的时候不知道怎么打表啊。。 View Code #include<cstdio> #include<cstring> #include<algorith…