面试高频考题——手撸一个 LRU 算法!

简介: 今天给大家讲一道面试中经常容易遇到的一道题:“手写一个 LRU 算法”。

今天给大家讲一道面试中经常容易遇到的一道题:“手写一个 LRU 算法”


LRU 就是 Least Recently Used 的缩写,翻译过来就是“最近最少使用”。 也就是说 LRU 算法会将最近最少用的缓存移除,让给最新使用的缓存。这是非常常见的一个缓存淘汰策略


利用好 LRU 算法,我们能够提高对热点数据的缓存效率,进而提升缓存服务的内存使用率。


那么如何实现呢?


其实,通过缓存的 key 获取其对应的缓存,我们应该能想到用哈希表来实现,毕竟这是一个键值对结构,可以在 O(1) 时间内获取 key 对应的缓存值。


但是,哈希表本身是无序的,因此,我们还需要一个类似于队列的数据结构来记录访问的先后顺序,将最近访问的数据放在头部(如下图),移除最后一项数据(最旧),我们可以用双向链表来实现数据的增删以及顺序的调整。


36.png


因此,我们结合“哈希表 + 双向链表”来实现 LRU 算法。其中:


双向链表按照被使用的顺序存储 kv 键值对,靠近头部的 kv 键值对是最近使用的,而靠近尾部的键值对是最久未使用的。

哈希表通过缓存的 key 映射到双向链表中的位置。我们可以在 O(1) 时间内定位到缓存的 key 所对应的 value 在链表中的位置。


37.png


对于 get 操作,判断 key 是否存在哈希表中:


若不存在,返回 -1。

若存在,则 key 对应的节点 node 是最近使用的节点。将该节点移动到双向链表的头部,最后返回该节点的值即可。


对于 put 操作,同样先判断 key 是否存在哈希表中:


若不存在,则创建一个新的 node 节点,放入哈希表中。然后在双向链表的头部添加该节点。接着判断双向链表节点数是否超过 capacity。若超过,则删除双向链表的尾部节点,并且删除哈希表中对应的项。

若存在,则更新 node 节点的值,然后该节点移动到双向链表的头部。


双向链表节点(哈希表的 value)的结构如下:


class Node {    int key;    int value;    Node prev;    Node next;    Node() {    }    Node(int key, int value) {        this.key = key;        this.value = value;    }}


你可能会问,哈希表的 value 为何还要存放 key?


这是因为,双向链表有一个删除尾节点的操作。我们定位到双向链表的尾节点,在链表中删除之后,还要找到该尾节点在哈希表中的位置,因此需要根据 value 中存放的 key,定位到哈希表的数据项,然后将其删除。


以下是 Java 版本代码的完整实现。


class LRUCache {    class Node {        int key;        int value;        Node prev;        Node next;        Node() {        }        Node(int key, int value) {            this.key = key;            this.value = value;        }    }    private int size;    private int capacity;    private Map<Integer, Node> cache;    /* 虚拟头节点 */    private Node head;    /* 虚拟尾节点 */    private Node tail;    public LRUCache(int capacity) {        this.size = 0;        this.capacity = capacity;        cache = new HashMap<>();        head = new Node();        tail = new Node();        head.next = tail;        tail.prev = head;    }    public int get(int key) {        Node node = cache.get(key);        if (node == null) {            return -1;        }        // 将最近这次访问的数据移到头部        moveToHead(node);        return node.value;    }    public void put(int key, int value) {        Node node = cache.get(key);        if (node == null) {            Node newNode = new Node(key, value);            cache.put(key, newNode);            // 将最近新增的数据放到头部            addToHead(newNode);            ++size;            // 若数据量超过设定的最大容量,移除尾部(最不常访问)的节点数据            if (size > capacity) {                Node tail = removeTail();                // 缓存淘汰,移除尾部节点数据                cache.remove(tail.key);                --size;            }        } else {            node.value = value;            moveToHead(node);        }    }    private void moveToHead(Node node) {        removeNode(node);        addToHead(node);    }    private void removeNode(Node node) {        node.prev.next = node.next;        node.next.prev = node.prev;    }    private void addToHead(Node node) {        node.next = head.next;        head.next = node;        node.next.prev = node;        node.prev = head;    }    private Node removeTail() {        Node node = tail.prev;        removeNode(node);        return node;    }}


目录
相关文章
|
2月前
|
算法 Java 数据库
美团面试:百亿级分片,如何设计基因算法?
40岁老架构师尼恩分享分库分表的基因算法设计,涵盖分片键选择、水平拆分策略及基因法优化查询效率等内容,助力面试者应对大厂技术面试,提高架构设计能力。
美团面试:百亿级分片,如何设计基因算法?
|
2月前
|
算法 前端开发 Java
数据结构与算法学习四:单链表面试题,新浪、腾讯【有难度】、百度面试题
这篇文章总结了单链表的常见面试题,并提供了详细的问题分析、思路分析以及Java代码实现,包括求单链表中有效节点的个数、查找单链表中的倒数第k个节点、单链表的反转以及从尾到头打印单链表等题目。
37 1
数据结构与算法学习四:单链表面试题,新浪、腾讯【有难度】、百度面试题
|
2月前
|
机器学习/深度学习 算法 Java
机器学习、基础算法、python常见面试题必知必答系列大全:(面试问题持续更新)
机器学习、基础算法、python常见面试题必知必答系列大全:(面试问题持续更新)
|
2月前
|
算法 Java 数据库
美团面试:百亿级分片,如何设计基因算法?
40岁老架构师尼恩在读者群中分享了关于分库分表的基因算法设计,旨在帮助大家应对一线互联网企业的面试题。文章详细介绍了分库分表的背景、分片键的设计目标和建议,以及基因法的具体应用和优缺点。通过系统化的梳理,帮助读者提升架构、设计和开发水平,顺利通过面试。
美团面试:百亿级分片,如何设计基因算法?
|
2月前
|
算法 Java 数据中心
探讨面试常见问题雪花算法、时钟回拨问题,java中优雅的实现方式
【10月更文挑战第2天】在大数据量系统中,分布式ID生成是一个关键问题。为了保证在分布式环境下生成的ID唯一、有序且高效,业界提出了多种解决方案,其中雪花算法(Snowflake Algorithm)是一种广泛应用的分布式ID生成算法。本文将详细介绍雪花算法的原理、实现及其处理时钟回拨问题的方法,并提供Java代码示例。
92 2
|
3月前
|
机器学习/深度学习 JavaScript 算法
面试中的网红虚拟DOM,你知多少呢?深入解读diff算法
该文章深入探讨了虚拟DOM的概念及其diff算法,解释了虚拟DOM如何最小化实际DOM的更新,以此提升web应用的性能,并详细分析了diff算法的实现机制。
|
15天前
|
算法
基于WOA算法的SVDD参数寻优matlab仿真
该程序利用鲸鱼优化算法(WOA)对支持向量数据描述(SVDD)模型的参数进行优化,以提高数据分类的准确性。通过MATLAB2022A实现,展示了不同信噪比(SNR)下模型的分类误差。WOA通过模拟鲸鱼捕食行为,动态调整SVDD参数,如惩罚因子C和核函数参数γ,以寻找最优参数组合,增强模型的鲁棒性和泛化能力。
|
21天前
|
机器学习/深度学习 算法 Serverless
基于WOA-SVM的乳腺癌数据分类识别算法matlab仿真,对比BP神经网络和SVM
本项目利用鲸鱼优化算法(WOA)优化支持向量机(SVM)参数,针对乳腺癌早期诊断问题,通过MATLAB 2022a实现。核心代码包括参数初始化、目标函数计算、位置更新等步骤,并附有详细中文注释及操作视频。实验结果显示,WOA-SVM在提高分类精度和泛化能力方面表现出色,为乳腺癌的早期诊断提供了有效的技术支持。
|
1天前
|
供应链 算法 调度
排队算法的matlab仿真,带GUI界面
该程序使用MATLAB 2022A版本实现排队算法的仿真,并带有GUI界面。程序支持单队列单服务台、单队列多服务台和多队列多服务台三种排队方式。核心函数`func_mms2`通过模拟到达时间和服务时间,计算阻塞率和利用率。排队论研究系统中顾客和服务台的交互行为,广泛应用于通信网络、生产调度和服务行业等领域,旨在优化系统性能,减少等待时间,提高资源利用率。
|
9天前
|
存储 算法
基于HMM隐马尔可夫模型的金融数据预测算法matlab仿真
本项目基于HMM模型实现金融数据预测,包括模型训练与预测两部分。在MATLAB2022A上运行,通过计算状态转移和观测概率预测未来值,并绘制了预测值、真实值及预测误差的对比图。HMM模型适用于金融市场的时间序列分析,能够有效捕捉隐藏状态及其转换规律,为金融预测提供有力工具。
下一篇
DataWorks