【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。

相关文章
|
4月前
|
Dart
Dart之集合详解(List、Set、Map)
Dart之集合详解(List、Set、Map)
|
4天前
|
算法
你对Collection中Set、List、Map理解?
你对Collection中Set、List、Map理解?
16 5
|
2月前
|
存储 安全 Java
java集合框架复习----(4)Map、List、set
这篇文章是Java集合框架的复习总结,重点介绍了Map集合的特点和HashMap的使用,以及Collections工具类的使用示例,同时回顾了List、Set和Map集合的概念和特点,以及Collection工具类的作用。
java集合框架复习----(4)Map、List、set
|
2月前
|
缓存 算法 前端开发
深入理解缓存淘汰策略:LRU和LFU算法的解析与应用
【8月更文挑战第25天】在计算机科学领域,高效管理资源对于提升系统性能至关重要。内存缓存作为一种加速数据读取的有效方法,其管理策略直接影响整体性能。本文重点介绍两种常用的缓存淘汰算法:LRU(最近最少使用)和LFU(最不经常使用)。LRU算法依据数据最近是否被访问来进行淘汰决策;而LFU算法则根据数据的访问频率做出判断。这两种算法各有特点,适用于不同的应用场景。通过深入分析这两种算法的原理、实现方式及适用场景,本文旨在帮助开发者更好地理解缓存管理机制,从而在实际应用中作出更合理的选择,有效提升系统性能和用户体验。
82 1
|
3月前
|
缓存 Python
在Python中,`functools`模块提供了一个非常有用的装饰器`lru_cache()`,它实现了最近最少使用(Least Recently Used, LRU)缓存策略。
在Python中,`functools`模块提供了一个非常有用的装饰器`lru_cache()`,它实现了最近最少使用(Least Recently Used, LRU)缓存策略。
|
2月前
|
存储 缓存 Java
|
2月前
|
存储 Java 索引
|
2月前
|
存储 缓存 算法
Python 从零开始实现一个简单的LRU缓存
Python 从零开始实现一个简单的LRU缓存
30 0
|
4月前
|
存储 安全 Java
Java集合详解:Set, Map, Vector, List的对比与联系
Java集合框架核心包括List、Set、Map和Vector。List允许重复元素,如ArrayList(适合读取)和LinkedList(适合插入删除)。Set不允许重复,有HashSet(无序)和TreeSet(排序)。Map存储键值对,HashMap(无序)和TreeMap(排序)。Vector是线程安全的ArrayList替代品,但在多线程环境下使用。选择集合类型应根据应用场景,如有序、无序、键值对需求及线程安全考虑。
|
4月前
|
存储 安全 Java
Java 集合(List、Set、Map 等)相关问答归纳再整理
HashMap 中使用键对象来计算 hashcode 值 HashSet 使用成员对象来计算 hashcode 值,对于两个对象来说hashcode 可能相同,所以 equals() 方法用来判断对象的相等性,如果两个对象不同的话,那么返回 false。 HashMap 比较快,因为是使用唯一的键来获取对象,HashSet 较 HashMap 来说比较慢。 4.1.3 HashMap 与 TreeMap
24 2