Leetcode|460. LFU 缓存(KV+KF+FK哈希链表+minFreq)

在这里插入图片描述
在这里插入图片描述

相关题目

《Leetcode|146. LRU 缓存机制(key2node哈希链表)》
《Leetcode|460. LFU 缓存(KV+KF+FK哈希链表+minFreq)》

解题逻辑

这道题虽然不难,但是逻辑较多,第一次手撕时由于漏掉2个逻辑没有AC,后来花了些时间debug完才AC,对于这种逻辑较多但不难的题,想要一次写对,还是不要偷懒。先不要着急写代码,把大致逻辑图画出来,这样能避免写代码时部分细节逻辑的遗忘

  • get(key)put(key, val)基本逻辑
    在这里插入图片描述

  • increaseFreq(key)基本逻辑在这里插入图片描述

  • removeMinFreq(key)insertNew(key, val)基本逻辑

完整代码

/**双链表数据结构**/
struct DListNode {
    int key;
    DListNode* prev;
    DListNode* next;
    DListNode(): key(0), prev(nullptr), next(nullptr) {}
    DListNode(int _key): key(_key), prev(nullptr), next(nullptr) {}
};
/**哈希链表数据结构**/
class HashList {
private:
    DListNode* head;
    DListNode* tail;
    unordered_map<int, DListNode*> key2node;  // 哈希链表
    int size;
public:
    HashList(int _key): size(0) {
        // 添加头尾空节点
        head = new DListNode();
        tail = new DListNode();
        head->next = tail;
        tail->prev = head;
        addRecentKey(_key);
    }

    void addRecentKey(int key) {
        auto node = new DListNode(key);
        // 插入1个最近使用节点
        node->prev = tail->prev;
        node->next = tail;
        tail->prev->next = node;
        tail->prev = node;
        // 更新哈希表
        key2node[key] = node;
        size++;
    }

    void removeAnyKey(int key) {
        auto& node = key2node[key];
        // 1.删链表节点
        node->prev->next = node->next;
        node->next->prev = node->prev;
        // 2.删哈希表节点
        key2node.erase(key);
        size--;
    }

    int removeLongestUsed() {
        int key = head->next->key;
        removeAnyKey(key);
        return key;
    }

    bool isEmpty() { return size == 0; } 
};

class LFUCache {
private:
    int capacity, size, minFreq;
    unordered_map<int, int> key2val;        // KV表
    unordered_map<int, int> key2freq;       // KF表
    unordered_map<int, HashList*> freq2key; // FK表
public:
    LFUCache(int _capacity): capacity(_capacity), size(0), minFreq(0) {}
    
    int get(int key) {
        if (!key2val.count(key)) return -1;
        increaseFreq(key);
        return key2val[key];
    }
    
    void put(int key, int value) {
        if (!capacity) return;
        if (key2val.count(key)) {
            key2val[key] = value;
            increaseFreq(key);
            return;
        }
        if (capacity == size) removeMinFreq();
        insertNew(key, value);
    }

    void increaseFreq(int key) {
        int freq = key2freq[key];
        // 1.更新KF表
        key2freq[key]++;
        // 2.更新FK表
        // 删除FK[freq]哈希链表对应的key, 删除后若哈希链表为空,则删除FK中的freq项
        freq2key[freq]->removeAnyKey(key);
        if (freq2key[freq]->isEmpty()) {
            freq2key.erase(freq);
            if (freq == minFreq) minFreq++;
        }
        // 若FK[freq + 1]存在,则直接添加
        if (freq2key.count(freq + 1)) freq2key[freq + 1]->addRecentKey(key);
        // 若FK[freq + 1]不存在,则初始化哈希链表
        else freq2key[freq + 1] = new HashList(key);
    }

    void removeMinFreq() {
        auto& hashList = freq2key[minFreq];
        // 1.删FK表
        int deleteKey = hashList->removeLongestUsed();
        if (hashList->isEmpty()) 
            freq2key.erase(minFreq);
        // 2.删KV表 
        key2val.erase(deleteKey);
        // 3.删KF表 
        key2freq.erase(deleteKey);
        size--;  // 无需更新minFreq, 因为本方法仅在put()添加新键值对时调用,minFreq = 1
    }

    void insertNew(int key, int value) {
        key2val[key] = value;
        key2freq[key] = 1;
        // 若freq2key[1]已存在,则直接按时序添加到哈希链表中
        if (freq2key.count(1)) freq2key[1]->addRecentKey(key);
        // 若freq2key[1]不存在,则为此初始化新哈希链表
        else freq2key[1] = new HashList(key);
        minFreq = 1;
        size++;
    }
};
/**
 * 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);
 */

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SL_World

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值