【LeetCode146】LRU缓存机制(list+unordered_map)

简介: 首先读懂题意:首先缓存最大容量初始化为capacity这么大,然后实现:put(key,val)存入键值对;get(key)获得键key对应的值val,如果键key不存在则返回-1。

1.题目

2.思路

首先读懂题意:

首先缓存最大容量初始化为capacity这么大,然后实现:

put(key,val)存入键值对;get(key)获得键key对应的值val,如果键key不存在则返回-1。

为了实现O(1)的时间负责度进行get和put。使用哈希链表的组合——哈希表查找快,链表插入快但查找慢。借用labuladong的一张图:

其中双向链表可以使用list容器实现。

list的成员函数(举例):

(1)push_front()在容器头部插入一个元素,和emplace_back()功能相当,但后者效率更高

(2)pop_front()删除容器头部的一个元素

(3)pop_back()删除容器尾部的一个元素

list的其他函数可以参考。

伪代码

(如果用java是用HashMap)

// key 映射到 Node(key, val)
HashMap<Integer, Node> map; //如果用java是用HashMap。
// Node(k1, v1) <-> Node(k2, v2)...
DoubleList cache;
int get(int key) {
    if (key 不存在) {
        return -1;
    } else {        
        将数据 (key, val) 提到开头;
        return val;
    }
}
void put(int key, int val) {
    Node x = new Node(key, val);
    if (key 已存在) {
        把旧的数据删除;
        将新节点 x 插入到开头;
    } else {
        if (cache 已满) {
            删除链表的最后一个数据腾位置;
            删除 map 中映射到该数据的键;
        } 
        将新节点 x 插入到开头;
        map 中新建 key 对新节点 x 的映射;
    }
}

3.代码(C++)

class LRUCache {
private:
    list<pair<int,int>>cache;
    unordered_map<int,list<pair<int,int>>::iterator>key2node;
    int cap;//cache的最大容量
public:
    LRUCache(int capacity):cap(capacity){
    }
    int get(int key) {//获取键key所对应的val值
        if(key2node.find(key)==key2node.end())
            return -1;
        //如果存在键key,即继续下步
        pair<int,int>node=*key2node[key];
        cache.erase(key2node[key]);//删除表中原有的结点
        cache.push_front(node);//将节点插入到链表头部
        key2node[key]=cache.begin();//易漏!不要只记得上面一步
        return node.second;//返回键key对应的val值
    }
    void put(int key, int val) {
        auto newNode =make_pair(key,val);
        if(key2node.count(key)){//该结点若已经存在,则删除旧结点(和上面类似)
            cache.erase(key2node[key]);
        }else{
            if(cap==cache.size()){//如果cache已满
                key2node.erase(cache.back().first);//删除key2node中映射到该数据的键
                cache.pop_back();//删除链表的最后一个位置
            }
        }
        cache.push_front(newNode);//插入新的结点到头部
        key2node[key]=cache.begin();//新建映射
    }
};
/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache* obj = new LRUCache(capacity);
 * int param_1 = obj->get(key);
 * obj->put(key,value);
 */

4.注意点

其中LRUCache函数注意这种初始化操作:

pubic:
  LRUCache(int capacity):cap(capacity){}

这是初始化的意思

等你进入构造函数 这个对象以及在内存中构造好了

比如有个vector 等你进去构造函数 这个 vector已经存在了 长度是0 在构造函数里如果要改这个vector长度,就浪费了。

这个冒号后面的就是在对象一开始生成的时候执行的

这个时候vector设置多长 ,那么 构造的时候就会直接构造多长 避免重复更改

(1)写法一

cap是一个成员变量

capxxx是一个构造函数的参数,用来初始化cap

首先,调用构造函数,然后传入参数capxxxp

操作系统就开始生成一个类的对象,然后在给这个对象分配内存的时候,用参数capxxx初始化了cap这个变量,然后构造结束了

(2)写法二

如果不用这个写法

用大括号里写cap=capxxx

那么就是,操作系统初始化类的对象,分配内存。

cap这个成员变量的内存已经有了,但是没有初始化,值是多少是一种未定义的行为。

这个时候进去构造函数内部,注意,这个时候类其实已经构造好了,内存啥的都搞定了,内存中已经有一块内存是这个类了。

这个时候cap=capxxx

操作系统找到这个变量的地址,然后赋值给他。

区别就是在构造变量的同时初始化。还是构造结束了,再赋值

对于一个int差别不大

但是对于一些复杂类型,差别就大了。

比如举例的vector

如果冒号写法,就相当于调用构造函数

vector(size),一开始就是有一定长度的

但是如果到了函数体再赋值,这个时候首先,他已经定义好了而且大小默是0,也就是说你要扩容,只能用扩容的方法,这个扩容肯定是耗费时间的。

而且因为已经构造好对象了,这个vector已经存在了,你就不能再用他的构造函数了,只能是给他赋值一个更大的。这其实是两重的构造,先构造一个大的vector。然后赋值给成员变量,成员变量拷贝。

然后退出的时候,这个函数内的vector还得析构.

reference

labuladong题解。

手写LUR。

相关文章
|
3月前
|
缓存 Java 数据库连接
mybatis复习05,mybatis的缓存机制(一级缓存和二级缓存及第三方缓存)
文章介绍了MyBatis的缓存机制,包括一级缓存和二级缓存的配置和使用,以及如何整合第三方缓存EHCache。详细解释了一级缓存的生命周期、二级缓存的开启条件和配置属性,以及如何通过ehcache.xml配置文件和logback.xml日志配置文件来实现EHCache的整合。
mybatis复习05,mybatis的缓存机制(一级缓存和二级缓存及第三方缓存)
|
7天前
|
算法
你对Collection中Set、List、Map理解?
你对Collection中Set、List、Map理解?
40 18
你对Collection中Set、List、Map理解?
|
4月前
|
缓存 应用服务中间件 nginx
Web服务器的缓存机制与内容分发网络(CDN)
【8月更文第28天】随着互联网应用的发展,用户对网站响应速度的要求越来越高。为了提升用户体验,Web服务器通常会采用多种技术手段来优化页面加载速度,其中最重要的两种技术就是缓存机制和内容分发网络(CDN)。本文将深入探讨这两种技术的工作原理及其实现方法,并通过具体的代码示例加以说明。
404 1
|
8天前
|
缓存 Java 数据库连接
MyBatis缓存机制
MyBatis提供两级缓存机制:一级缓存(Local Cache)默认开启,作用范围为SqlSession,重复查询时直接从缓存读取;二级缓存(Second Level Cache)需手动开启,作用于Mapper级别,支持跨SqlSession共享数据,减少数据库访问,提升性能。
19 1
|
2月前
|
缓存 JavaScript 前端开发
vue2基础组件通信案例练习:把案例Todo-list改写成本地缓存
vue2基础组件通信案例练习:把案例Todo-list改写成本地缓存
47 5
|
2月前
|
缓存 JavaScript 前端开发
vue2基础组件通信案例练习:把案例Todo-list改写成本地缓存
vue2基础组件通信案例练习:把案例Todo-list改写成本地缓存
19 1
|
2月前
|
存储 缓存 负载均衡
Nginx代理缓存机制
【10月更文挑战第2天】
93 4
|
2月前
|
存储 缓存 NoSQL
深入理解后端缓存机制的重要性与实践
本文将探讨在后端开发中缓存机制的应用及其重要性。缓存,作为提高系统性能和用户体验的关键技术,对于后端开发来说至关重要。通过减少数据库访问次数和缩短响应时间,缓存可以显著提升应用程序的性能。本文将从缓存的基本概念入手,介绍常见的缓存策略和实现方式,并通过实例展示如何在后端开发中有效应用缓存技术。最后,我们将讨论缓存带来的一些挑战及其解决方案,帮助您在实际项目中更好地利用缓存机制。
|
3月前
|
算法
你对Collection中Set、List、Map理解?
你对Collection中Set、List、Map理解?
39 5
|
3月前
|
存储 缓存 Android开发
Android RecyclerView 缓存机制深度解析与面试题
本文首发于公众号“AntDream”,详细解析了 `RecyclerView` 的缓存机制,包括多级缓存的原理与流程,并提供了常见面试题及答案。通过本文,你将深入了解 `RecyclerView` 的高性能秘诀,提升列表和网格的开发技能。
76 8