LRU&LFU实现方式C++

yuanheci 2023年09月25日 149次浏览

参考文章

1、LRU

https://leetcode.cn/problems/lru-cache/

typedef pair<int, int> PII;
class LRUCache {
public:
    int cap, cnt;
    //pair<第几个插入的,val>
    int l[200010], r[200010], h[200010], idx; //h用来通过key快速找到结点
    PII e[200010]; 
    //维护当前最后一个节点是第几个插入的

    void insert(int k, PII x){
        e[idx] = x;
        l[idx] = k;
        r[idx] = r[k];
        l[r[k]] = idx;
        r[k] = idx++;
        cnt++;
    }

    void remove(int k){
        r[l[k]] = r[k];
        l[r[k]] = l[k];
        cnt--;
    }

    LRUCache(int capacity) {
        this->cap = capacity;
        memset(l, -1, sizeof l);
        memset(r, -1, sizeof r);
        memset(h, -1, sizeof h);
        r[0] = 1, l[1] = 0;  //0,1是哨兵
        idx = 2;
        cnt = 0;
    }
    
    //用头插法维护这个双链表!
    int get(int key) {
        if(h[key] == -1) return -1;
        //把链表中老的点删除
        PII t = e[h[key]];
        remove(h[key]);
        h[key] = idx;
        insert(0, t);  //会更新idx
        return t.second;
    }
    
    void put(int key, int value) {
        if(h[key] != -1) remove(h[key]); 
        insert(0, {key, value});
        h[key] = idx - 1;  //更新h[key]
        if(cnt > cap){  //需要逐出
            //删除h中
            h[e[l[1]].first] = -1;
            remove(l[1]);
        }
    }
};

/**
 * 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);
 */


2、LFU

https://leetcode.cn/problems/lfu-cache/description/

//参考思路  https://leetcode.cn/problems/lfu-cache/solutions/977393/lru-lfu-by-mats9693-sy1x/

const int N = 200010;
class LFUCache {
public:
    struct Node{
        int key, value, freq;
    };

    int h[N], mp[N], l[N], r[N], idx, cap, n;  //mp[]:用key索引idx
    int ct[N];   //freq对应的结点的个数
    Node e[N];  //e[]:用idx索引,
    int pu;

    LFUCache(int capacity) {
        this->cap = capacity;
        memset(h, -1, sizeof h);
        memset(l, -1, sizeof l);
        memset(r, -1, sizeof r);
        memset(mp, -1, sizeof mp);
        r[0] = 1, l[1] = 0, idx = 2;
        pu = 0;
        n = 0;
    }

    void insert(int k, Node x){
        e[idx] = x;
        r[idx] = r[k];
        l[idx] = k;
        r[k] = idx;
        l[r[idx]] = idx;
        idx++;
        n++;
    }

    void remove(int k){
        r[l[k]] = r[k];
        l[r[k]] = l[k];
        n--;
    }
    
    int get(int key) {
        if(mp[key] == -1) return -1;  
        auto t = e[mp[key]]; 
        //如何知道当前结点右边的idx
        int rr = r[mp[key]];
        bool flag = false;
        if(ct[t.freq + 1] > 0){  //插入到freq+1部分的最前面
            //先删除掉freq的这个结点
            remove(mp[key]);
            int pos = h[t.freq + 1];
            insert(l[pos], {t.key, t.value, t.freq + 1});  
            // insert(l[pos], {t.key, t.value, t.freq});
            flag = true;
        }
        //没有freq+1的部分,但是有freq部分(不止自己一个),那么插入到freq部分的最前面
        else if(ct[t.freq] > 1 && e[h[t.freq]].key != t.key){ 
            remove(mp[key]);
            int pos = h[t.freq];
            insert(l[pos], {t.key, t.value, t.freq + 1});  
            // insert(l[pos], {t.key, t.value, t.freq});  
            flag = true;
        }
        //更新h
        if(flag) h[t.freq + 1] = idx - 1, mp[key] = idx - 1;  //因为更换了位置,所以mp也要更改
        else {
            h[t.freq + 1] = mp[key] = h[t.freq];
            e[mp[key]].freq++;   //这种情况也要++
        }

        if(ct[t.freq] <= 1){  //没有其他freq = n的结点
            h[t.freq] = -1;    //注意:这里key是不会消失的,只是ct变化了而已
        } 
        else if(e[h[t.freq]].key == t.key){  //如果ct > 1且它原先是作为头部存在的
            //错误想法:
            // h[t.freq] = r[idx - 1];
            // h[t.freq] = r[h[t.freq + 1]];  
            //**正确想法**: 要用它原先的右边!
            h[t.freq] = rr;
        }
        ct[t.freq]--;
        ct[t.freq + 1]++;
        pu = mp[key];
        // e[pu].freq++;     //将缓存次数+1
        return t.value;
    }   
    
    void put(int key, int value) {
        if(mp[key] != -1) {
            get(key);
            //map[key]可能已经更改了,所以下面注释代码是错误的
            // e[mp[key]].value = value;
            e[pu].value = value;
            return;
        }
        //key不存在
        if(n >= cap){ //逐出最后一个结点
            auto t = e[l[1]];
            remove(l[1]);
            mp[t.key] = -1;  //一定要把key清掉
            if(--ct[t.freq] <= 0) h[t.freq] = -1;//如果由于逐出key导致ct->0,那么mp也要清除
        }
        //插入,此时freq = 1
        if(ct[1] > 0){  //需要移动到freq=1部分的开头
            int pos = h[1];    
            insert(l[pos], {key, value, 1});
        }
        else{  //插入到末尾
            insert(l[1], {key, value, 1});
        }
        h[1] = idx - 1;
        ct[1]++;
        mp[key] = idx - 1;
    }
};

/**
 * 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);
 */