面试题常考: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;
    }
};
相关推荐
岁月变迁呀1 小时前
Redis梳理
数据库·redis·缓存
唐诺2 小时前
几种广泛使用的 C++ 编译器
c++·编译器
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭2 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
XH华2 小时前
初识C语言之二维数组(下)
c语言·算法
南宫生2 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
不想当程序猿_2 小时前
【蓝桥杯每日一题】求和——前缀和
算法·前缀和·蓝桥杯
落魄君子3 小时前
GA-BP分类-遗传算法(Genetic Algorithm)和反向传播算法(Backpropagation)
算法·分类·数据挖掘
冷眼看人间恩怨3 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
菜鸡中的奋斗鸡→挣扎鸡3 小时前
滑动窗口 + 算法复习
数据结构·算法
红龙创客3 小时前
某狐畅游24校招-C++开发岗笔试(单选题)
开发语言·c++