导读:本期聚焦于小伙伴创作的《如何实现C++高性能LFU缓存淘汰机制并优化最小频率查找寻址》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何实现C++高性能LFU缓存淘汰机制并优化最小频率查找寻址》有用,将其分享出去将是对创作者最好的鼓励。

LFU(Least Frequently Used)缓存淘汰机制的核心逻辑是优先淘汰访问频率最低的数据,当缓存满时,把使用次数最少的元素移除。传统LFU实现中,每次淘汰都需要遍历所有元素的频率来找到最小值,时间复杂度较高,在高并发场景下会成为性能瓶颈。我们可以通过优化数据结构,将最小频率查找的时间复杂度从O(n)降到O(1)。

如何实现C++高性能LFU缓存淘汰机制并优化最小频率查找寻址

核心数据结构设计

要实现高性能的LFU缓存,需要组合使用三种结构:

  • 哈希表 key_to_node:存储键到缓存节点的映射,用于O(1)时间找到对应缓存元素
  • 哈希表 freq_to_list:存储频率到双向链表的映射,每个频率对应一个双向链表,链表中是该频率下的所有缓存节点,方便按频率顺序维护节点
  • 最小频率变量 min_freq:记录当前缓存中的最小访问频率,淘汰时直接从这个频率对应的链表中取尾部节点即可

缓存节点需要包含键、值、访问频率、以及所在双向链表的前后指针,具体定义如下:

struct LFUNode {
    int key;
    int value;
    int freq;
    LFUNode* prev;
    LFUNode* next;
    LFUNode(int k, int v) : key(k), value(v), freq(1), prev(nullptr), next(nullptr) {}
};

双向链表操作封装

为了方便操作每个频率对应的双向链表,我们封装一个双向链表类,支持添加节点到头部、移除节点、移除尾部节点(淘汰用)的操作:

class DoubleList {
private:
    LFUNode* head;
    LFUNode* tail;
    int size;
public:
    DoubleList() {
        head = new LFUNode(-1, -1);
        tail = new LFUNode(-1, -1);
        head->next = tail;
        tail->prev = head;
        size = 0;
    }

    // 添加节点到链表头部
    void addToHead(LFUNode* node) {
        node->next = head->next;
        node->prev = head;
        head->next->prev = node;
        head->next = node;
        size++;
    }

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

    // 移除尾部节点(最久未访问的,用于淘汰)
    LFUNode* removeTail() {
        if (size == 0) return nullptr;
        LFUNode* node = tail->prev;
        removeNode(node);
        return node;
    }

    int getSize() {
        return size;
    }
};

LFU缓存核心实现

接下来实现LFU缓存的主体类,包含构造函数、get方法、put方法,以及频率更新和最小频率维护的逻辑:

#include <unordered_map>
using namespace std;

class LFUCache {
private:
    unordered_map<int, LFUNode*> key_to_node;
    unordered_map<int, DoubleList*> freq_to_list;
    int capacity;
    int min_freq;

    // 更新节点的频率
    void updateFreq(LFUNode* node) {
        int old_freq = node->freq;
        // 从旧频率的链表中移除节点
        freq_to_list[old_freq]->removeNode(node);
        // 如果旧频率的链表为空,且旧频率是最小频率,更新最小频率
        if (freq_to_list[old_freq]->getSize() == 0) {
            delete freq_to_list[old_freq];
            freq_to_list.erase(old_freq);
            if (min_freq == old_freq) {
                min_freq++;
            }
        }
        // 更新节点频率
        node->freq++;
        int new_freq = node->freq;
        // 如果新频率对应的链表不存在,创建新链表
        if (freq_to_list.find(new_freq) == freq_to_list.end()) {
            freq_to_list[new_freq] = new DoubleList();
        }
        // 将节点添加到新频率链表的头部
        freq_to_list[new_freq]->addToHead(node);
    }

public:
    LFUCache(int cap) : capacity(cap), min_freq(0) {}

    int get(int key) {
        if (capacity == 0) return -1;
        // 如果键不存在,返回-1
        if (key_to_node.find(key) == key_to_node.end()) {
            return -1;
        }
        LFUNode* node = key_to_node[key];
        // 更新节点频率
        updateFreq(node);
        return node->value;
    }

    void put(int key, int value) {
        if (capacity == 0) return;
        // 如果键已经存在,更新值并调整频率
        if (key_to_node.find(key) != key_to_node.end()) {
            LFUNode* node = key_to_node[key];
            node->value = value;
            updateFreq(node);
            return;
        }
        // 如果缓存已满,淘汰最小频率的节点
        if (key_to_node.size() == capacity) {
            // 从最小频率对应的链表中移除尾部节点
            DoubleList* min_freq_list = freq_to_list[min_freq];
            LFUNode* removed_node = min_freq_list->removeTail();
            // 如果链表为空,删除链表并清理映射
            if (min_freq_list->getSize() == 0) {
                delete min_freq_list;
                freq_to_list.erase(min_freq);
            }
            // 从键映射中删除被淘汰的节点
            key_to_node.erase(removed_node->key);
            delete removed_node;
        }
        // 创建新节点
        LFUNode* new_node = new LFUNode(key, value);
        // 新节点频率为1,添加到频率1的链表中
        if (freq_to_list.find(1) == freq_to_list.end()) {
            freq_to_list[1] = new DoubleList();
        }
        freq_to_list[1]->addToHead(new_node);
        // 更新键映射
        key_to_node[key] = new_node;
        // 新插入的节点频率为1,所以最小频率重置为1
        min_freq = 1;
    }

    ~LFUCache() {
        // 清理所有节点和链表
        for (auto& pair : key_to_node) {
            delete pair.second;
        }
        for (auto& pair : freq_to_list) {
            delete pair.second;
        }
    }
};

优化点说明

上述实现的核心优化在于最小频率查找的O(1)复杂度

  • 传统LFU实现需要遍历所有节点找最小频率,时间复杂度O(n),本实现通过维护min_freq变量,直接记录当前最小频率,淘汰时直接从对应链表取节点
  • 每次更新节点频率时,会检查旧频率的链表是否为空,如果为空且旧频率等于min_freq,就将min_freq加1,保证min_freq始终有效
  • 新插入的节点频率固定为1,所以每次插入后min_freq都会重置为1,符合LFU的逻辑

使用示例

以下是该LFU缓存的使用示例,验证功能正确性:

#include <iostream>
using namespace std;

int main() {
    LFUCache* cache = new LFUCache(2);
    cache->put(1, 1);
    cache->put(2, 2);
    cout << cache->get(1) << endl; // 返回1,访问后键1的频率变为2
    cache->put(3, 3); // 淘汰键2(频率为1)
    cout << cache->get(2) << endl; // 返回-1,键2已被淘汰
    cout << cache->get(3) << endl; // 返回3
    cache->put(4, 4); // 淘汰键3(频率为1)
    cout << cache->get(1) << endl; // 返回1
    cout << cache->get(3) << endl; // 返回-1,键3已被淘汰
    cout << cache->get(4) << endl; // 返回4
    delete cache;
    return 0;
}

该实现的所有操作(get、put)时间复杂度均为O(1),空间复杂度为O(capacity),适合对性能要求较高的缓存场景使用。

LFU缓存C++缓存淘汰算法最小频率查找优化修改时间:2026-06-26 10:18:40

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