面试题常考: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;
    }
};
相关推荐
YOU OU6 小时前
Redis基础常识与命令
数据库·redis·缓存
To_OC6 小时前
LC 51 N 皇后:我以为难的是回溯,结果栽在了对角线下标
javascript·算法·leetcode
2401_841495647 小时前
【操作系统】进程同步与互斥实验报告
c++·算法·操作系统·进程·并发·同步·互斥
fqbqrr7 小时前
2607C++,soui与安卓
c++·soui
触底反弹7 小时前
🔥 2026 大模型选择指南:别再只看 Benchmark 了,这些维度才是关键!
人工智能·面试
醇氧9 小时前
CountDownLatch / CyclicBarrier / Semaphore 面试高频问答清单
前端·面试·职场和发展
吳所畏惧10 小时前
宝塔面板Redis密码修改指南:SSH命令修改 vs 面板UI界面修改,哪个更靠谱?
运维·服务器·数据库·redis·缓存·ssh
fqbqrr10 小时前
2607C++,使用微软detours勾挂工具
c++
爱刷碗的苏泓舒11 小时前
PPP-AR 中的参考星选取:数学原理、评价指标与切换处理
算法·gnss·模糊度固定·ppp-ar·星间单差·参考星·卫星端偏差