面试题常考: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;
    }
};
相关推荐
奶糖趣多多6 分钟前
Redis知识点
数据库·redis·缓存
浮生如梦_1 小时前
Halcon基于laws纹理特征的SVM分类
图像处理·人工智能·算法·支持向量机·计算机视觉·分类·视觉检测
CoderIsArt1 小时前
Redis的三种模式:主从模式,哨兵与集群模式
数据库·redis·缓存
励志成为嵌入式工程师3 小时前
c语言简单编程练习9
c语言·开发语言·算法·vim
师太,答应老衲吧3 小时前
SQL实战训练之,力扣:2020. 无流量的帐户数(递归)
数据库·sql·leetcode
捕鲸叉4 小时前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer4 小时前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
Peter_chq4 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
wheeldown4 小时前
【数据结构】选择排序
数据结构·算法·排序算法
青花瓷5 小时前
C++__XCode工程中Debug版本库向Release版本库的切换
c++·xcode