Leetcode 460. LFU 缓存机制(困难)

460. LFU 缓存机制(困难)

题目:

get(key) 方法会去缓存中查询键 key,如果 key 存在,则返回 key 对应的 val,否则返回 -1。

put(key, value) 方法插入或修改缓存。如果 key 已存在,则将它对应的值改为 val;如果 key 不存在,则插入键值对 (key, val)

当缓存达到容量 capacity 时,则应该在插入新的键值对之前,删除使用频次(后文用 freq 表示)最低的键值对。如果 freq 最低的键值对有多个,则删除其中最旧的那个。

 

思路:

labuladong

map<key,node> kv保存node

map<freq,list<node>> fv保存freq对应的node双向链表,按照头部最旧、尾部最新排列

minFreq记录当前所有key的最小Freq

node记录key、val、freq

 

1、当get时,如果存在,先拿到node,然后对node进行update,将其freq+1,同时从oldFreq的list删除,并添加到newFreq的list中。此时如果minFreq==oldFreq&&fv[minFreq].empty,证明minFreq的node已经没有,minFreq+1

2、当put时

  • 如果capacity为0,则什么都不做
  • 如果key存在,更新node值,并update(node)
  • 如果kv.size==capacity,removeMinFreqKey。也就是从minFreq对应的list中移除front,并从kv中移除
  • 添加k v,minFreq=1

 

struct Node{
    int key;
    int val;
    int freq;
    Node* pre;
    Node* next;
    Node(int k,int v){
        key=k;
        val=v;
        freq=1;
        pre=nullptr;
        next=nullptr;
    }
};
class DoubleList {
public:
    DoubleList(){
        length=0;
        head=new Node(0,0);
        tail=new Node(0,0);
        head->next=tail;
        tail->pre=head;
    }
    void addLast(Node* node){
        tail->pre->next=node;
        node->pre=tail->pre;
        node->next=tail;
        tail->pre=node;
        length++;
    }
    Node* delFront(){
        Node* node=head->next;
        head->next=node->next;
        node->next->pre=head;
        length--;
        return node;
    }
    void delNode(Node* node){
        Node* pre=node->pre;
        Node* next=node->next;
        pre->next=next;
        next->pre=pre;
        length--;
    }
    bool empty(){
        return length==0;
    }
private:
    int length;
    Node* head;
    Node* tail;
};
class LFUCache {
public:
    LFUCache(int capacity) {
        this->capacity=capacity;
        minFreq=0;
    }
    
    int get(int key) {
        unordered_map<int, Node*>::iterator it=kv.find(key);
        if(it==kv.end()) {
            return -1;
        }
        Node* node=it->second;
        // 增加 key 对应的 freq
        increaseFreq(node);
        int val=node->val;
        return val;
    }
    
    void put(int key, int value) {
        // 如果容量为0,则什么都不做
        if(capacity<=0){
            return;
        }
        /* 若 key 已存在,修改对应的 val 即可 */
        if(kv.count(key)==1){
            Node* node=kv[key];
            // 增加 key 对应的 freq
            increaseFreq(node);
            node->val=value;
            return;
        }
        /* key 不存在,需要插入 */
        /* 容量已满的话需要淘汰一个 freq 最小的 key */
        if(kv.size()==capacity){
            removeMinFreq();
        }
        // 添加新 key val
        addKey(key,value);
        // 插入新 key 后最小的 freq 肯定是 1
        minFreq=1;
    }
private:
    // 增加node的freq
    void increaseFreq(Node* node){
        int oldFreq=node->freq;
        // 更新node自身的freq
        node->freq++;
        // 从oldfreq对应的list中删除node
        fv[oldFreq].delNode(node);
        // 在新freq对应的list中添加node
        fv[oldFreq+1].addLast(node);
        // 如果oldFreq==1且对应node为空,则minFreq+1
        if(oldFreq==minFreq&&fv[oldFreq].empty()){
            minFreq++;
        }
    }
    // 除去freq最小最旧的node
    void removeMinFreq(){
        Node* node=fv[minFreq].delFront();
        kv.erase(node->key);
        // 因为此时后边跟着的操作是addKey,所以minFreq肯定为1
    }
    //添加key
    void addKey(int key, int val){
        Node* node=new Node(key,val);
        kv.insert(make_pair(key,node));
        fv[1].addLast(node);
    }
private:
    int capacity;
    // key->node
    unordered_map<int, Node*> kv;
    // freq -> list<node>
    unordered_map<int, DoubleList> fv;
    int minFreq;
};

 

posted @ 2022-02-24 17:25  鸭子船长  阅读(28)  评论(0编辑  收藏  举报