【LeetCode 面试经典150题】146. LRU Cache LRU缓存

本文介绍了如何设计和实现一个遵循LRU策略的缓存数据结构,使用双向链表和哈希表相结合的方式,确保get和put操作的平均时间复杂度为O(1),并提供了相关示例和解题思路。
摘要由CSDN通过智能技术生成

146. LRU Cache

题目大意

Design a data structure that follows the constraints of a Least Recently Used (LRU) cache.

Implement the LRUCache class:

  • LRUCache(int capacity) Initialize the LRU cache with positive size capacity.
  • int get(int key) Return the value of the key if the key exists, otherwise return -1.
  • void put(int key, int value) Update the value of the key if the key exists. Otherwise, add the key-value pair to the cache. If the number of keys exceeds the capacity from this operation, evict the least recently used key.

The functions get and put must each run in O(1) average time complexity.

中文释义

设计一个遵循最近最少使用(LRU)缓存约束的数据结构。

实现 LRUCache 类:

  • LRUCache(int capacity) 用正整数容量初始化 LRU 缓存。
  • int get(int key) 如果键存在,则返回键的值;否则,返回 -1。
  • void put(int key, int value) 如果键存在,更新其值。否则,将键值对添加到缓存中。如果此操作导致键的数量超出容量,那么淘汰最近最少使用的键。

getput 函数必须都具有 O(1) 的平均时间复杂度。

示例

示例 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); // LRU键是2,淘汰键2,缓存是 {1=1, 3=3}
lRUCache.get(2); // 返回 -1 (未找到)
lRUCache.put(4, 4); // LRU键是1,淘汰键1,缓存是 {4=4, 3=3}
lRUCache.get(1); // 返回 -1 (未找到)
lRUCache.get(3); // 返回 3
lRUCache.get(4); // 返回 4

限制条件

  • 容量范围为 1 <= capacity <= 3000
  • 键的范围为 0 <= key <= 104
  • 值的范围为 0 <= value <= 105
  • 最多将对 getput 调用 2 * 10^5 次。

解题思路

方法

该方案使用双数据结构:一个双向链表(list)和一个哈希表(unordered_map),来实现一个 LRU 缓存。双向链表用于维护键值对的顺序,而哈希表用于快速访问链表中的节点。

  1. 初始化

    • 初始化一个双向链表 cacheList 来存储 (key, value) 对。
    • 初始化一个哈希表 cacheMap 用于存储键到 cacheList 中对应迭代器的映射。
  2. 移动节点到链表头

    • 实现一个 moveToHead 方法,用于将已存在的节点移动到链表的头部。
  3. 获取数据

    • get 方法首先检查键是否存在于哈希表中,若不存在返回 -1。
    • 如果键存在,使用 moveToHead 方法将节点移到链表头,并返回对应的值。
  4. 存放数据

    • put 方法首先检查键是否存在于哈希表中。
    • 如果键已存在,更新其值并使用 moveToHead 方法移动到链表头部。
    • 如果键不存在,插入新的键值对到链表头,并更新哈希表。
    • 如果插入新元素后链表大小超过容量,则删除链表尾部的元素并从哈希表中移除对应的键。

代码

class LRUCache {
private:
    int capacity;
    list<pair<int, int>> cacheList; // 存储 (key, value) 对
    unordered_map<int, list<pair<int, int>>::iterator> cacheMap; // Key -> cacheList 中对应迭代器的映射

    // 将节点移动到链表头部
    void moveToHead(int key, int value) {
        cacheList.erase(cacheMap[key]);
        cacheList.push_front(make_pair(key, value));
        cacheMap[key] = cacheList.begin();
    }

public:
    LRUCache(int capacity) : capacity(capacity) {}

    int get(int key) {
        if (cacheMap.find(key) == cacheMap.end()) {
            return -1; // key 不存在
        }
        // 将节点移动到链表头部,并返回对应的 value
        moveToHead(key, cacheMap[key]->second);
        return cacheMap[key]->second;
    }
    
    void put(int key, int value) {
        if (cacheMap.find(key) != cacheMap.end()) {
            // 更新已有 key 的 value 并移动到链表头部
            moveToHead(key, value);
        } else {
            // 插入新 key-value 对
            cacheList.push_front(make_pair(key, value));
            cacheMap[key] = cacheList.begin();
            if (cacheList.size() > capacity) {
                // 如果超出容量,删除链表尾部元素
                cacheMap.erase(cacheList.back().first);
                cacheList.pop_back();
            }
        }
    }
};
  • 17
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值