C++ 实现LFU缓存机制

实现LFU(最不经常使用缓存算法)

leetcode题目链接

请你为 最不经常使用(LFU)缓存算法设计并实现数据结构。

实现 LFUCache 类:

  • LFUCache(int capacity) - 用数据结构的容量 capacity 初始化对象
  • int get(int key) - 如果键存在于缓存中,则获取键的值,否则返回 -1。
  • void put(int key, int value) - 如果键已存在,则变更其值;如果键不存在,请插入键值对。当缓存达到其容量时,则应该在插入新项之前,使最不经常使用的项无效。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,应该去除最久未使用 的键
class LFUCache {
private:
    // 双向链表节点
    struct DLinkNode {
        int key, val, freq;

        // 节点构造函数
        DLinkNode(int key, int val, int freq) : key(key), val(val), freq(freq) {}
    };

    // 最小频率,表示最近最少使用的链表
    int minFreq;
    // 缓存的容量
    int capacity;

    // 哈希表:存储(key, 链表节点在链表中的位置)
    std::unordered_map<int, std::list<DLinkNode>::iterator> key_table;
    // 哈希表:存储(key, val, freq)
    std::unordered_map<int, std::list<DLinkNode>> freq_table;

public:

LFUCache(int capacity) {
    this->minFreq = 0;
    this->capacity = capacity;
    key_table.clear();
    freq_table.clear();
}

int get(int key) {
    if (capacity == 0) {
        return -1;
    }

    // key_table中是否存在该key
    auto iter = key_table.find(key);
    // 如果不存在,返回-1
    if (iter == key_table.end()) {
        return -1;
    }

    // 如果存在该key,
    // node为iter指向的节点,即key对应的节点
    auto node = iter->second;
    // 该节点的val和freq值
    int val = node->val;
    int freq = node->freq;
    // 因为get(key)属于操作,所以该节点频率加1
    // 将该节点从哈希表原链表freq_table[freq]中删除
    freq_table[freq].erase(node);

    // 如果删除该节点后,freq_table[freq]链表为空
    if (freq_table[freq].empty()) {
        // 则从freq_table中删除该频率freq对应的双向链表
        freq_table.erase(freq);
        // 如果最小频率minFreq等于freq,因为freq使用后频率加1,
        // 所以minFreq最小频率也要更新为加1
        if (minFreq == freq) {
            minFreq += 1;
        }
    }

    // 头部表示最近使用,尾部表示最近最久未使用
    // 将该节点添加到链表freq_table[freq+1]的头部,并将该节点的频率更新
    freq_table[freq + 1].push_front(DLinkNode(key, val, freq + 1));
    // 同时更新key_table中key所对应节点在新链表freq_table[freq+1]的头部位置
    key_table[key] = freq_table[freq + 1].begin();

    return val;
}

void put(int key, int value) {
    if (capacity == 0) {
        return;
    }

    // key_table中是否存在该key
    auto iter = key_table.find(key);

    // 如果不存在该key对应的节点,就将该节点插入到频率对应的双向链表头部
    if (iter == key_table.end()) {
        // 首先判断当前缓存大小是否达到容量
        if (key_table.size() == capacity) {
            // 如果当前缓存大小已经达到容量,则需要先删除最近最少使用的节点
            // 在哈希表中找到最近最少使用的双向链表freq_table[minFreq]
            // 因为最近加入的节点都在头部,所以最近最少使用的是尾部节点
            auto it = freq_table[minFreq].back();
            // 删除key_table中最近最少使用的节点
            key_table.erase(it.key);
            // 从双向链表freq_table[minFreq]中删除尾节点
            freq_table[minFreq].pop_back();

            // 判断此时频率最小的双向链表是否为空
            if (freq_table[minFreq].empty()) {
                // 如果为空则删除频率最小的双向链表
                freq_table.erase(minFreq);
            }
        }

        // 因为新插入节点的频率为1,所有将其插入到freq_table[1]链表中
        freq_table[1].push_front(DLinkNode(key, value, 1));
        // 同时更新key_table中key所对应的节点在频率链表中相应的位置
        key_table[key] = freq_table[1].begin();
        // 更新最小频率为1
        minFreq = 1;
    } else {
        // 如果存在该key,
        // node为iter指向的节点,即key对应的节点
        auto node = iter->second;
        // 获得该节点的freq值
        int freq = node->freq;
        // 因为该节点因使用后频率加1,所以从原频率链表中删除该节点
        freq_table[freq].erase(node);

        // 判断节点原频率链表是否为空
        if (freq_table[freq].empty()) {
            // 如果为空,则从哈希表中删除该频率链表
            freq_table.erase(freq);
            // 如果最小频率等于节点原频率,则将最小频率更新加1
            if (minFreq == freq) {
                minFreq += 1;
            }
        }

        // 将该节点频率加1后添加到频率加1的双向链表的头部
        freq_table[freq + 1].push_front(DLinkNode(key, value, freq + 1));
        // 同时更新key所对应的节点在相应频率链表中的头部位置
        key_table[key] = freq_table[freq + 1].begin();
    }
}

};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值