目录

  • 大概思路
  • 时间空间复杂度分析
  • 指针操作具体细节
    • 代码
    • 双向链表设计
    • 私有成员变量设计:
    • 构造函数和析构函数设计:
    • 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);
}

感想

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

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

  1. 分享实录 | 深度学习技术红利下的代码补全

    简介: 在软件研发过程中,"代码补全"至关重要,它可以有效的提升开发效率.减少拼写错误和输入代码量.本期<云效说码>分享邀请斑马网络技术专家旭伦分享了<技术红利下 ...

  2. html没有代码补全 vscode_借助clangd实现VSCode C++代码补全

    YCM推荐选项 由于C++语言的复杂性,根据语义对C++进行代码补全并非易事.最近浏览YouCompleteMe项目,发现它已经开始向用户推荐使用clangd作为C++ Completer.与libc ...

  3. Sublime Text 2 - 性感无比的代码编辑器!程序员必备神器!跨平台支持Win/Mac/Linux,支持32与64位,支持各种流行编程语言的语法高亮、代码补全等...

    Sublime Text 2 - 性感无比的代码编辑器!程序员必备神器!跨平台支持Win/Mac/Linux,支持32与64位,支持各种流行编程语言的语法高亮.代码补全等-- 语法高亮.代码提示补全. ...

  4. Delphi 2007 代码补全、语句提示的快捷键是什么?

    为了帮助网友解决"Delphi 2007 代码补全.语句提"相关的问题,中国学网通过互联网对"Delphi 2007 代码补全.语句提"相关的解决方案进行了整理 ...

  5. Vim使用Vundle安装代码补全插件(YouCompleteMe)

    Vim使用Vundle安装代码补全插件(YouCompleteMe) 安装 Vundle 它的使用方法很简单,安装一个插件只需要在 ~/.vimrc 按照规则中添加 Plugin 的名称,某些需要添加 ...

  6. 重磅!革命级AI代码补全工具,这款撸码利器让程序员界沸腾了!

    我们平时写代码的时候,多少都会依赖编辑器的代码补全功能,敲几个字母就能补全一个词.可是这么多年过去了,语言升级了很多次,而代码提示却没有升级,还是只能限定在一个词,毫无意义地按照字典表顺序排列,这对于 ...

  7. 在windows中python安装sit-packages路径位置 在Pycharm中导入opencv不能自动代码补全问题

    一.在windows中python安装sit-packages路径位置 C:\Users\shl\AppData\Local\Programs\Python\Python36\Lib\site-pac ...

  8. devc代码补全没效果_从零开始写文本编辑器(二十八):自动补全(上)

    前言 我本没打算这么早就写"自动补全"功能的. 但是在写XML资源编辑时,为了实现自动引用已有资源@string/xxx,需要一个合适的列表来让我选择.这样能防止拼写错误. 也就是 ...

  9. 阿里云开发大会——体验云效智能代码补全

    简介:5月29日阿里云开发大会「智能开发与高效运维」分论坛上,云效请来12位阿里巴巴开发运维领域专家,聚焦分享阿里云赋能开发者的系列工具和实践.现场动手训练营吸引近千位开发者体验云原生开发部署.代码安 ...

最新文章

  1. javaweb友好的删除提示语句!
  2. SAP RETAIL初阶之事务代码MP83 显示一个预测参数文件
  3. iOS 学习 - 13.微信分享链接、QQ 分享图片
  4. 【Python】创建数组[[0]*n]*m与[[0 for _ in range(n)] for _ in range(m)]的区别
  5. oracle数据库的select,Oracle数据库--基本的select语句
  6. linux moveto回收站,linux添加回收站(脚本)
  7. IPv4的路由选择计算步骤
  8. 堆栈平衡:估计这是最详细的讲解堆栈平衡的了
  9. 80386汇编_全局描述表GDT介绍
  10. Ring buffer streaming in general - how to imple...
  11. 写教案时,PDF怎么转换成PPT?用迅读PDF大师,超简单
  12. 快递查询网站php源码,全国快递查询PHP源码单号自动识别支持国内上百家快递
  13. 途胜怎样与android手机互联,现代途胜车载蓝牙怎么连接,途胜手机互联映射教程...
  14. g++的英文版使用说明和选项
  15. vrf路由泄露原理和简单示例
  16. 数据库的锁机制理解和运用
  17. MySql数据类型-读书笔记
  18. [算法]递归(尾递归和非尾递归)
  19. 以下是一些常用的上位机开发工具:
  20. 华为云classroom Java练习

热门文章

  1. 程序员有哪些可以写博客的网站?
  2. 把python37添加到环境变量配置_关于在win 10上成功创建一个Django项目时遇到django-admin的手动配置环境变量问题。...
  3. bootbox.js
  4. canvas学习和滤镜实现
  5. 我对CSS选择器的认识
  6. React hook 中的数据获取
  7. 【C++】满二叉树问题
  8. 2018-2019-1 20165203 《信息安全系统设计基础》第六周学习总结
  9. 第八章 CTE 递归 及 分组汇总 高级部分(多维数据集)
  10. Ubuntu 14.10 -- 异次元软件世界