0
点赞
收藏
分享

微信扫一扫

LRU缓存

小铺有酒一两不够 2022-03-19 阅读 87

题目描述

请你设计并实现一个满足  LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:

    LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
    int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
    void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。

函数 get 和 put 必须以 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 <= capacity <= 3000
    0 <= key <= 10000
    0 <= value <= 105
    最多调用 2 * 105 次 get 和 put

思路

坦白说这道题其实我并没有什么思路,甚至数据结构我都没有想好,时间复杂度平均还要O(1),这表明一定要用哈希,但是我们在set时,还要存储数据,map<int,pair<int,int>>???--可是我们容量超过限制时还需要pop数据,那么我又该如何判断该pop掉哪个数据?莫非是使用队列?每一次pop最前面的元素吗?那么队列中的每一个元素的数据类型是什么样子的才能保证O(1)的时间复杂度呢?

数据结构

 看到答案使用的数据结构时恍然大悟,hashmap+链表.

初始化

我们首先设置一个元素类型为pair<int,int>的链表结构_lru,再设置一个map<int,list<pair<int,int>>::iterator>的hash数据结构_table,这样就能保证对于每一个key值,我们可以得到一个指向list节点的指针.当然,最后还需要一个全局变量保存容量_capacity.

put函数

我们需要先判断这个key值是否是存在的,如果存在,那么我们在更新完_lru链表相应节点的value值后还需要将相应节点移动到_lru链表头,一共两步然后return,但是如果是不存在的,那么我们需要先给_lru链表设置一个新的头结点,然后再令_table[key]=_lru.begin(),指向这个头结点.如果当前_lru.size>capacity,那么我们就需要在_table中erase(_lru.back().first),再删除_lru的back节点(直接pop_back),但是注意二者的顺序是不可以改变的.

set函数

set函数其实比put函数简单一些,当map中找到了key值后,重置其value值,然后移动链表相应节点到链表头结点即可,最后返回相应节点的second即可.但是如果没有找到,就说明找不到当前节点,我们直接返回-1即可.

注意事项

 1)容量是_lru的数量,只有当容量大于capacity时,才执行_lru.pop_back() 操作

2)_table类型是unordered_map<int,list::iterator>类型(中间省略了参数),而不是unordered_map<int,list>类型,所以我们_table.find()返回的结果是一个pair<int,list::iterator>类型(记为iter),那么我们在取或者在设置的过程中操作的其实是iter->second (这是一个pair<key,value>类型的结构),在get的过程中return iter->second->second,在set的过程中也是对iter->second->second进行赋值.

代码

#include<unordered_map>
#include<list>
#include<utility>
using namespace std;

class LRUCache {
public:
    LRUCache(int capacity) {
        _capacity = capacity;
    }

    int get(int key) {
        if (_table.find(key) != _table.end()) {
            auto i = _table.find(key);      //i是 pair<int,list<pair<int, int>>::iterator>::iterator类型
            _lru.splice(_lru.begin(), _lru, i->second);
            return i->second->second;
        }
        return -1;
    }

    void put(int key, int value) {
        auto i = _table.find(key);
        if (i != _table.end()) {
            _lru.splice(_lru.begin(), _lru, i->second);
            i->second->second = value;
            return; 
        }
        _lru.emplace_front( key,value );
        _table[key] = _lru.begin();
        if (_lru.size() > _capacity) {
            _table.erase(_lru.back().first);
            _lru.pop_back();
        }
    }

private:
    int _capacity ;
    list<pair<int, int>> _lru;
    unordered_map<int, list<pair<int, int>>::iterator> _table;
};

/**
 * 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);
 */

 

举报

相关推荐

0 条评论