面试题常考: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 ,则应该 逐出 最久未使用的关键字。

函数 getput 必须以 O(1) 的平均时间复杂度运行。

示例:

输入

"LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"

\[2\], \[1, 1\], \[2, 2\], \[1\], \[3, 3\], \[2\], \[4, 4\], \[1\], \[3\], \[4\]

输出

null, null, null, 1, null, -1, null, -1, 3, 4

解释

LRUCache lRUCache = new LRUCache(2);

lRUCache.put(1, 1); // 缓存是 {1=1}

lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}

lRUCache.get(1); // 返回 1

lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}

lRUCache.get(2); // 返回 -1 (未找到)

lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}

lRUCache.get(1); // 返回 -1 (未找到)

lRUCache.get(3); // 返回 3

lRUCache.get(4); // 返回 4

思路:

1.题目中存放的数据是键值对形式的,所以我们可以采用哈希表(unordered_map)来实现

2.同时,题目要求get()、put()的时间复杂度为O(1),也就是能够快速插入、删除元素,来确保时间复杂度低,最佳的数据结构应该是链表,这里用双向链表最高效

所以,我们需要添加一个双向链表的结构体和无序map来对数据实现LRU缓存。

详细过程参考下面代码:

Code:

cpp 复制代码
class LRUCache {
public:
	//双链表的结构体
    struct Node
    {
        int key;
        int val;
        //前驱和后继指针
        Node * prev,*next;
        //构造函数
        Node():key(0),val(0),prev(nullptr),next(nullptr){}
        Node(int m_key,int m_val):key(m_key),val(m_val),prev(nullptr),next(nullptr){}
    };
    unordered_map<int,Node*> map;//哈希表,用来存储键值对
    Node* head;//头节点
    Node* tail;//尾节点
    
    int m_capacity;//总容量
    int size;//哈希表当前容量
    LRUCache(int capacity):m_capacity(capacity),size(0) {
        //初始化头尾节点
        head=new Node();
        tail=new Node();
        //构建双向链表
        head->next=tail;
        tail->prev=head;
    }
    //获取函数
    int get(int key) {
        //如果哈希表中不存在键为key,直接返回-1
        if(!map.count(key))
        {
            return -1;
        }
        //存在key
        //获取链表的节点
        Node* node=map[key];
        remove(node);//删除节点
        AddNodeToHead(node);//将当前节点移至头节点之后
        return node->val;//返回节点的值
    }
    
    void put(int key, int value) {
        //如果当前key值已存在
        if(map.count(key))
        {
            //获取节点
            Node* node=map[key];
            //改变节点的值为新的value
            node->val=value;
            remove(node);//删除节点
            AddNodeToHead(node);//将节点移至头节点之后
        }
        //不存在,则加入到哈希表中
        else
        {
            //判断容量是否已满
            if(size==m_capacity)//满了
            {
                //获取最近最久未使用的节点,也就是尾节点的前驱节点
                Node* removed=tail->prev;
                //从哈希表中移除该节点
                map.erase(removed->key);
                //删除节点
                remove(removed);
                //当前容量--
                size--;
            }
            //创建新节点
            Node* node=new Node(key,value);
            AddNodeToHead(node);//将节点移至头节点之后
            map[key]=node;//加入哈希表中
            size++;//当前容量++
        }
    }
    //删除节点函数
    void remove(Node* node)
    {
        node->prev->next=node->next;
        node->next->prev=node->prev;
    }
    //将节点移至头节点之后
    void AddNodeToHead(Node* node)
    {
        node->prev=head;
        node->next=head->next;
        head->next->prev=node;
        head->next=node;
    }
};
相关推荐
SweetCode1 分钟前
裴蜀定理:整数解的奥秘
数据结构·python·线性代数·算法·机器学习
ゞ 正在缓冲99%…14 分钟前
leetcode76.最小覆盖子串
java·算法·leetcode·字符串·双指针·滑动窗口
xuanjiong15 分钟前
纯个人整理,蓝桥杯使用的算法模板day2(0-1背包问题),手打个人理解注释,超全面,且均已验证成功(附带详细手写“模拟流程图”,全网首个
算法·蓝桥杯·动态规划
uhakadotcom24 分钟前
Julia语言:高性能数值计算的新星
面试·架构·github
Zhichao_9733 分钟前
【UE5 C++课程系列笔记】33——商业化Json读写
c++·ue5
惊鸿.Jh34 分钟前
【滑动窗口】3254. 长度为 K 的子数组的能量值 I
数据结构·算法·leetcode
明灯L35 分钟前
《函数基础与内存机制深度剖析:从 return 语句到各类经典编程题详解》
经验分享·python·算法·链表·经典例题
uhakadotcom39 分钟前
Helm 简介与实践指南
后端·面试·github
碳基学AI41 分钟前
哈尔滨工业大学DeepSeek公开课:探索大模型原理、技术与应用从GPT到DeepSeek|附视频与讲义免费下载方法
大数据·人工智能·python·gpt·算法·语言模型·集成学习
补三补四44 分钟前
机器学习-聚类分析算法
人工智能·深度学习·算法·机器学习