当前位置: 首页 > news >正文

【C++】【常见面试题】最简版带大小和超时限制的LRU缓存实现

关键实现要点

  1. 数据结构选择

    • 双向链表:维护访问顺序
    • 哈希表:快速查找节点
  2. 淘汰策略

    • 容量满时删除最久未使用的节点
    • 访问时自动检查并删除超时节点
  3. 时间管理

    • 使用steady_clock记录节点时间戳
    • 访问时更新节点时间

完整代码实现

#include <unordered_map>
#include <list>
#include <chrono>using namespace std;
using namespace std::chrono;class TimedLRUCache {
private:// 节点结构:存储键、值和访问时间struct Node {int key;int value;steady_clock::time_point timestamp;Node(int k, int v) : key(k), value(v), timestamp(steady_clock::now()) {}};int capacity_;                      // 缓存容量int timeout_ms_;                    // 超时时间(毫秒)list<Node> cache_list_;             // 双向链表,维护访问顺序unordered_map<int, decltype(cache_list_.begin())> key_map_;  // 哈希表,快速查找public:// 构造函数:设置容量和超时时间TimedLRUCache(int capacity, int timeout_ms = 0) : capacity_(capacity), timeout_ms_(timeout_ms) {}// 获取值,不存在返回-1int get(int key) {auto it = key_map_.find(key);if (it == key_map_.end()) return -1;// 检查是否超时if (isExpired(it->second)) {removeNode(it->second);return -1;}// 更新访问时间并移动到链表头部it->second->timestamp = steady_clock::now();cache_list_.splice(cache_list_.begin(), cache_list_, it->second);return it->second->value;}// 插入或更新值void put(int key, int value) {auto it = key_map_.find(key);if (it != key_map_.end()) {// 键已存在,更新值和访问时间it->second->value = value;it->second->timestamp = steady_clock::now();cache_list_.splice(cache_list_.begin(), cache_list_, it->second);return;}// 检查容量,删除最久未使用的节点if (cache_list_.size() >= capacity_) {removeLRUNode();}// 插入新节点到链表头部cache_list_.emplace_front(key, value);key_map_[key] = cache_list_.begin();}// 检查键是否存在且未超时bool contains(int key) {auto it = key_map_.find(key);if (it == key_map_.end()) return false;if (isExpired(it->second)) {removeNode(it->second);return false;}return true;}// 获取当前缓存大小int size() const {return cache_list_.size();}// 获取缓存容量int capacity() const {return capacity_;}private:// 检查节点是否超时bool isExpired(decltype(cache_list_.begin()) iter) {if (timeout_ms_ == 0) return false; // 0表示永不过期auto now = steady_clock::now();auto elapsed = duration_cast<milliseconds>(now - iter->timestamp);return elapsed.count() > timeout_ms_;}// 删除指定节点void removeNode(decltype(cache_list_.begin()) iter) {key_map_.erase(iter->key);cache_list_.erase(iter);}// 删除最久未使用的节点void removeLRUNode() {if (cache_list_.empty()) return;// 从尾部开始查找未超时的节点删除for (auto it = cache_list_.rbegin(); it != cache_list_.rend(); ++it) {auto list_it = prev(it.base()); // 反向迭代器转正向迭代器if (!isExpired(list_it)) {removeNode(list_it);return;}}// 如果所有节点都超时,删除最后一个if (!cache_list_.empty()) {removeNode(prev(cache_list_.end()));}}
};

使用示例

#include <iostream>
#include <thread>int main() {// 创建缓存:容量3,超时时间1000msTimedLRUCache cache(3, 1000);// 测试基本功能cache.put(1, 100);cache.put(2, 200);cache.put(3, 300);cout << "初始状态 - 大小: " << cache.size() << endl; // 3cout << "获取键2: " << cache.get(2) << endl;         // 200// 测试超时功能this_thread::sleep_for(milliseconds(1500));cout << "超时后获取键1: " << cache.get(1) << endl;   // -1 (已超时)cout << "超时后大小: " << cache.size() << endl;      // 2 (自动清理)// 测试LRU淘汰cache.put(4, 400);cache.put(5, 500);cout << "插入新键后大小: " << cache.size() << endl;  // 3return 0;
}

关键点梳理

1. 数据结构设计

  • 双向链表:维护节点的访问顺序,最近访问的在头部
  • 哈希表:提供O(1)时间复杂度的节点查找
  • 节点结构:包含键、值和时间戳三要素

2. 淘汰策略实现

// 关键代码:删除最久未使用节点
void removeLRUNode() {// 从尾部开始查找(最久未使用)for (auto it = cache_list_.rbegin(); it != cache_list_.rend(); ++it) {auto list_it = prev(it.base());if (!isExpired(list_it)) {  // 找到第一个未超时的节点removeNode(list_it);return;}}// 所有节点都超时,删除最后一个if (!cache_list_.empty()) {removeNode(prev(cache_list_.end()));}
}

3. 时间管理机制

// 关键代码:超时检查
bool isExpired(decltype(cache_list_.begin()) iter) {if (timeout_ms_ == 0) return false; // 永不过期auto now = steady_clock::now();auto elapsed = duration_cast<milliseconds>(now - iter->timestamp);return elapsed.count() > timeout_ms_;
}

4. 访问顺序维护

// 关键代码:节点访问时的处理
it->second->timestamp = steady_clock::now();  // 更新时间
cache_list_.splice(cache_list_.begin(), cache_list_, it->second); // 移动到头部

核心优势

  1. 时间复杂度:get和put操作都是O(1)
  2. 空间复杂度:O(capacity),只存储有限节点
  3. 自动清理:访问时自动检查并删除超时节点
  4. 线程安全:单线程环境下安全使用

这个实现平衡了功能完整性和代码简洁性,适合需要基础缓存功能的场景。

http://www.dtcms.com/a/549730.html

相关文章:

  • CSAPP实验2:Bomb
  • [人工智能-大模型-118]:模型层 - RNN状态记忆是如何实现的?是通过带权重的神经元,还是通过张量?
  • 手机做网站需要多少天国外代理ip地址和端口
  • Unity-WebGL开发——用IIS(Internet Information Services)部署webGL工程
  • 怎么仿制别人的网站一个网站的建设流程有哪些
  • 学生信息管理系统oracle
  • Angular【router路由】
  • 如何网站建设网页宣传片拍摄报价明细
  • 曲阜做网站智能建造概论
  • 嘉峪关网站seo英文商城网站
  • 易混淆的点:栈的增长方向 和 缓冲区内的数据写入方向是相反的
  • 全流程掌握生态环评核心技术——涵盖生物量模拟、生物多样性计算、脆弱度评价及公路铁路、机场、水利项目实战
  • 【Embedded System】嵌入式C语言基础知识
  • PsTools 学习笔记(7.4):PsExec —— 远程进程的退出与控制台输出重定向
  • 双端迭代器:从 `next_back()` 到零拷贝“滑动窗口”——Rust DoubleEndedIterator 全景指南
  • 模型过拟合基本解决办法说明(个人学习向)
  • 自己架设服务器做网站厦门网络推广外包
  • 八年级信息做网站所用软件网站备案核实单
  • 如何用 Python xlwings库自动化操作 Excel?
  • 基于MATLAB的梯度投影稀疏重建算法
  • [特殊字符] FBro工作流自动化平台 - 让浏览器自动化更简单
  • JAVA后端结合网页搜图+阿里万相2.5实现自动化修图与返回
  • 和平区网站制作手机企业网站怎么做
  • 如何在好医生网站做二类学分dede中英文网站切换
  • Kubernetes 实战入门内容
  • 变量声明与可变性
  • 前端生产部署完全指南:从零到精通
  • 做外贸的人常用的网站wordpress视频站模板下载
  • Oracle数据库安装(Windows)
  • 跳转指令介绍