【leetcode】缓存淘汰策略题目总结

146. LRU 缓存

我们使用了一个双向链表cache来存储数据,同时使用一个哈希表hash_map来映射键和链表节点的迭代器。当调用get(key)函数时,我们首先检查hash_map中是否存在该key,如果存在则将之前位置移到链表头部并返回值;当调用put(key, value)函数时,我们先检查hash_map中是否存在该key,存在的话将该节点从链表中删除,不管存在与否都需要考虑是否需要删除旧数据(缓存已满的情况)。

cpp 复制代码
class LRUCache {
private:
    int _capacity;
    list<pair<int, int>> _cache;
    unordered_map<int, list<pair<int, int>>::iterator> _hashmap;

public:
    LRUCache(int capacity) {
        _capacity = capacity;
    }
    
    int get(int key) {
        if (_hashmap.find(key) == _hashmap.end()) {
            return -1;
        }
        auto iter = _hashmap[key];
        int value = iter->second;
        _cache.erase(iter);
        _cache.push_front({key, value});
        _hashmap[key] = _cache.begin();
        return value;
    }
    
    void put(int key, int value) {
        if (_hashmap.find(key) != _hashmap.end()) {
            auto iter = _hashmap[key];
            _cache.erase(iter);
        } else if (_cache.size() >= _capacity) {
            auto hashKey = _cache.back().first;
            _hashmap.erase(hashKey);
            _cache.pop_back();
        }
        _cache.push_front({key, value});
        _hashmap[key] = _cache.begin();
    }
};

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache* obj = new LRUCache(capacity);
 * int param_1 = obj->get(key);
 * obj->put(key,value);
 */

460. LFU 缓存

我们定义两个哈希表,

第一个 freq_table 以频率 freq 为索引,每个索引存放一个双向链表,这个链表里存放所有使用频率为 freq 的缓存,缓存里存放三个信息,分别为键 key,值 value,以及使用频率 freq。

第二个 key_table 以键值 key 为索引,每个索引存放对应缓存在 freq_table 中链表里的内存地址,这样我们就能利用两个哈希表来使得两个操作的时间复杂度均为 O(1)。

同时需要记录一个当前缓存最少使用的频率 minFreq,这是为了删除操作服务的。

cpp 复制代码
struct Node
{
    int key;
    int val;
    int freq;
    Node(int _key, int _val, int _freq) : key(_key), val(_val), freq(_freq) {}
};

class LFUCache {
private:
    int capacity;
    int minfreq;
    unordered_map<int, list<Node>> freq_table;
    unordered_map<int, list<Node>::iterator> key_table;

public:
    LFUCache(int _capacity) {
        minfreq = 0;
        capacity = _capacity;
        key_table.clear();
        freq_table.clear();
    }
    
    int get(int key) {
        // 啥也没有
        if (capacity == 0) {
            return -1; 
        }

        // 没有这个key
        if (!key_table.count(key)) {
            return -1;
        }

        auto iter = key_table[key];
        int val  = iter->val;
        int freq = iter->freq;

        //查到删除旧的,增加新的
        freq_table[freq].erase(iter);
        //中间要判断一下此freq的链表是不是空了
        if (freq_table[freq].size() == 0) {
            //删除
            freq_table.erase(freq);
            //更新 minfreq
            if(minfreq == freq) minfreq += 1;
        }

        //插入新的
        freq_table[freq + 1].push_front(Node(key, val, freq + 1));
        key_table[key] = freq_table[freq + 1].begin();
        return val;
    }
    
    void put(int key, int value) {
        // 啥也没有
        if (capacity == 0) {
            return; 
        }

        // 没有的话,加进去
        if (!key_table.count(key))
        {
            // 存储key的数量满了
            if (key_table.size() == capacity) 
            {
                // 删掉频率最小的最早使用的
                auto node = freq_table[minfreq].back();
                int k = node.key;
                key_table.erase(k);
                freq_table[minfreq].pop_back();
                // 同样,该链空了,删除
                if (freq_table[minfreq].size() == 0) {
                    freq_table.erase(minfreq);
                }
            }
            // 插入          
            freq_table[1].push_front(Node(key, value, 1));
            key_table[key] = freq_table[1].begin();
            minfreq = 1; //注意这里要更新minfreq,容易出现错误

        } else {//存在
            // 更新
            // 先删,后频率增加,插入
            auto node = key_table[key];
            int freq = node->freq;
            freq_table[freq].erase(node);

            // 同样链空了,删除
            if(freq_table[freq].size() == 0)
            {
                freq_table.erase(freq);
                if (minfreq == freq) {
                    minfreq += 1;
                }
            }
            // 插入
            freq_table[freq + 1].push_front(Node(key, value, freq + 1));
            key_table[key] = freq_table[freq + 1].begin();
        }

    }
};
/**
 * Your LFUCache object will be instantiated and called as such:
 * LFUCache* obj = new LFUCache(capacity);
 * int param_1 = obj->get(key);
 * obj->put(key,value);
 */

FIFO 缓存

先进先出,明显使用哈希+队列

cpp 复制代码
#include <iostream>
#include <queue>
#include <unordered_map>

class FIFOCache {
private:
    std::queue<int> fifoQueue; // 存放key
    std::unordered_map<int, int> cache; // 存放key-value
    int capacity;

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

    int get(int key) {
        if (cache.find(key) != cache.end()) {
            return cache[key];
        }
        return -1;
    }

    void put(int key, int value) {
        if (cache.size() >= capacity) {
            int keyToRemove = fifoQueue.front();
            fifoQueue.pop();
            cache.erase(keyToRemove);
        }

        cache[key] = value;
        fifoQueue.push(key);
    }
};

int main() {
    FIFOCache cache(2);

    cache.put(1, 1);
    cache.put(2, 2);
    std::cout << cache.get(1) << std::endl; // 输出:1
    cache.put(3, 3);
    std::cout << cache.get(2) << std::endl; // 输出:-1,因为元素2被淘汰了

    return 0;
}
相关推荐
EterNity_TiMe_13 分钟前
【论文复现】(CLIP)文本也能和图像配对
python·学习·算法·性能优化·数据分析·clip
机器学习之心24 分钟前
一区北方苍鹰算法优化+创新改进Transformer!NGO-Transformer-LSTM多变量回归预测
算法·lstm·transformer·北方苍鹰算法优化·多变量回归预测·ngo-transformer
yyt_cdeyyds35 分钟前
FIFO和LRU算法实现操作系统中主存管理
算法
雯0609~42 分钟前
网页F12:缓存的使用(设值、取值、删除)
前端·缓存
alphaTao1 小时前
LeetCode 每日一题 2024/11/18-2024/11/24
算法·leetcode
kitesxian1 小时前
Leetcode448. 找到所有数组中消失的数字(HOT100)+Leetcode139. 单词拆分(HOT100)
数据结构·算法·leetcode
VertexGeek2 小时前
Rust学习(八):异常处理和宏编程:
学习·算法·rust
石小石Orz2 小时前
Three.js + AI:AI 算法生成 3D 萤火虫飞舞效果~
javascript·人工智能·算法
菠萝咕噜肉i3 小时前
超详细:Redis分布式锁
数据库·redis·分布式·缓存·分布式锁
jiao_mrswang3 小时前
leetcode-18-四数之和
算法·leetcode·职场和发展