导读:本期聚焦于小伙伴创作的《C++如何实现带unordered_map优化的LRU双向链表缓存系统》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《C++如何实现带unordered_map优化的LRU双向链表缓存系统》有用,将其分享出去将是对创作者最好的鼓励。

LRU即最近最少使用,是一种常见的缓存淘汰策略,当缓存容量达到上限时,会优先移除最久没有被访问的数据。在C++中实现高效的LRU缓存系统,通常需要结合双向链表和哈希表,这里选择unordered_map来优化查询效率,避免单纯使用链表带来的遍历开销。

C++如何实现带unordered_map优化的LRU双向链表缓存系统

LRU缓存系统设计思路

整体设计分为两个核心部分:

  • 双向链表:用来维护节点的访问顺序,链表头部是最近访问的节点,尾部是最久未访问的节点,容量满时直接移除尾部节点即可。
  • unordered_map:键为缓存的键,值为双向链表节点的指针,通过哈希表可以在O(1)时间内定位到对应节点,避免链表的线性查询。

节点结构设计

双向链表的节点需要存储键、值、前驱指针和后继指针,方便后续的插入、删除操作,节点定义如下:

// 双向链表节点定义
struct Node {
    int key;    // 缓存键
    int value;  // 缓存值
    Node* prev; // 前驱节点指针
    Node* next; // 后继节点指针
    // 构造函数
    Node(int k = 0, int v = 0) : key(k), value(v), prev(nullptr), next(nullptr) {}
};

LRU缓存类核心成员

LRU缓存类需要维护容量、哈希表、双向链表的头尾哑节点,哑节点可以简化边界条件的处理,不需要额外判断头尾节点为空的情况:

#include <unordered_map>
#include <iostream>

class LRUCache {
private:
    int capacity;                       // 缓存容量
    std::unordered_map<int, Node*> cacheMap; // 哈希表,键到链表节点的映射
    Node* head;                         // 链表头哑节点,最近访问的节点放在头节点之后
    Node* tail;                         // 链表尾哑节点,最久未访问的节点放在尾节点之前

    // 将节点移动到链表头部(表示最近访问)
    void moveToHead(Node* node) {
        // 先移除节点原有位置
        removeNode(node);
        // 插入到头节点之后
        addToHead(node);
    }

    // 移除指定节点
    void removeNode(Node* node) {
        node->prev->next = node->next;
        node->next->prev = node->prev;
    }

    // 将节点插入到链表头部
    void addToHead(Node* node) {
        node->next = head->next;
        node->prev = head;
        head->next->prev = node;
        head->next = node;
    }

    // 移除链表尾部节点(最久未访问的节点)
    Node* removeTail() {
        Node* node = tail->prev;
        removeNode(node);
        return node;
    }

public:
    // 构造函数,初始化容量和哑节点
    LRUCache(int cap) : capacity(cap) {
        head = new Node();
        tail = new Node();
        head->next = tail;
        tail->prev = head;
    }
};

核心操作实现

get操作

get操作的逻辑是:先通过unordered_map查询键是否存在,如果不存在返回-1;如果存在,说明该节点被访问了,需要将其移动到链表头部,然后返回值。

int get(int key) {
    // 查询哈希表
    auto it = cacheMap.find(key);
    if (it == cacheMap.end()) {
        // 键不存在,返回-1
        return -1;
    }
    // 键存在,将节点移动到头部表示最近访问
    Node* node = it->second;
    moveToHead(node);
    return node->value;
}

put操作

put操作的逻辑是:先查询键是否存在,如果存在,更新值并移动到头部;如果不存在,创建新节点插入头部,同时加入哈希表,如果容量超过上限,移除尾部节点并从哈希表中删除对应的键。

void put(int key, int value) {
    auto it = cacheMap.find(key);
    if (it != cacheMap.end()) {
        // 键已存在,更新值并移动到头部
        Node* node = it->second;
        node->value = value;
        moveToHead(node);
    } else {
        // 键不存在,创建新节点
        Node* newNode = new Node(key, value);
        // 插入到头部
        addToHead(newNode);
        // 加入哈希表
        cacheMap[key] = newNode;
        // 判断容量是否超限
        if (cacheMap.size() > capacity) {
            // 移除尾部最久未访问的节点
            Node* tailNode = removeTail();
            // 从哈希表中删除对应的键
            cacheMap.erase(tailNode->key);
            // 释放节点内存
            delete tailNode;
        }
    }
}

完整测试代码

以下是完整的可运行测试代码,验证缓存的功能是否符合预期:

int main() {
    LRUCache* lruCache = new LRUCache(2);
    lruCache->put(1, 1);
    lruCache->put(2, 2);
    std::cout << lruCache->get(1) << std::endl; // 返回1,访问后1变为最近使用
    lruCache->put(3, 3); // 容量满,移除最久未访问的2
    std::cout << lruCache->get(2) << std::endl; // 返回-1,2已被移除
    lruCache->put(4, 4); // 容量满,移除最久未访问的1
    std::cout << lruCache->get(1) << std::endl; // 返回-1,1已被移除
    std::cout << lruCache->get(3) << std::endl; // 返回3
    std::cout << lruCache->get(4) << std::endl; // 返回4
    return 0;
}

性能说明

这种实现方式下,get、put操作的时间复杂度都是O(1),因为unordered_map的查询、插入、删除平均时间复杂度是O(1),双向链表的插入、删除操作也是O(1),避免了单纯使用双向链表时查询需要遍历O(n)的问题,适合对缓存性能要求较高的场景。

注意事项

  • 哑节点的使用可以大幅简化边界处理,不需要判断链表为空或者操作头尾节点的特殊情况。
  • 节点删除时要同步释放内存,避免内存泄漏,同时要记得从unordered_map中移除对应的键。
  • 如果缓存存储的是大对象,节点的值可以改为指针或者智能指针,减少拷贝开销。

LRU_cacheunordered_map双向链表C++修改时间:2026-06-24 23:33:41

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。