LeetCode460:LFU缓存-hard

1.题目

在这里插入图片描述
参考题解:https://leetcode-cn.com/problems/lfu-cache/solution/shuo-de-ming-bai-by-jason-2/

2. 数据结构

在这里插入图片描述
数据结构:哈希表,链表+链表,cnt2key是一个频次链表,其内部每个结点包含一个频次值与另一个链表(存放相同频次的key,尾部为最久没有被访问过的)。

list<pair<int, list<int>>> cnt2key;
struct node{
	int value;
	int cnt;
	list<pair<int, list<int>>>::iterator cnt_pos;
	list<int>::iterator key_pos;
}
unordered_map<int,node> index;//key->int, value->node

哈希表中存放的是key就是要查找的key,value是一个node,node中包含插入时key对应的值,频次,该频次在cnt2key链表中的位置cnt_pos,以及key在cnt_pos对应链表中的位置key_pos。

3. 算法加code

  • get操作的步骤:
    1)如果key存在,则在哈希表中查找key对应的node,node->cnt++,将node放在频次为cnt+1的链表中,如果频次为cnt+1的链表不存在,则新建一个链表,然后将node从频次为cnt的链表中删除,如果删除之后频次为cnt的链表为空,则在cnt2key中删除频次为cnt的链表。返回node->value
    2)如果key不存在,返回-1.
  • put操作的步骤:
    1)如果key存在,则更新key的频次,执行get操作中的1)。
    2)如果key不存在:
    2.1)如果cap超过容量,则将频次最低的链表中的最后一个(最不经常使用的)删除,如果删除之后为空,则将该频次的链表从cnt2key中删除。
    2.2)获取当前cnt2key的头结点,如果头结点为空或者头结点对应的频次不是1,则新建一个频次为1的链表,将key对应的node插入。
class LFUCache {
public:
    list<pair<int,list<int>>> cnt2key;
    typedef list<pair<int,list<int>>>::iterator llit;//iterator of list list
    typedef list<int>::iterator lit;//list iterator
    struct node {
        int value;
        int cnt;//频次
        llit cnt_pos;//频次链表中的位置
        lit  key_pos;
    };
    unordered_map<int,node> cache;
    int cap;
    LFUCache(int capacity) : cap(capacity) {
    }
    
    int get(int key) {
        if(cache.count(key)){ //key存在
            auto& n = cache[key];
            update(key, n);
            return n.value;
        } else {
            return -1;
        }
    }
    
    void put(int key, int value) {
        if(cache.count(key)){
            auto& n = cache[key];
            n.value = value;//更新value
            update(key, n);//更新频次
        } else if(cap > 0) {
            if(cache.size() >= cap){ //需要移除频次最低的链表中的最不常用的
                auto it = cnt2key.begin();//频次最低的链表
                int k = it->second.back();//要删除的key
                it->second.pop_back();
                if(it->second.empty()){
                    cnt2key.erase(it);
                }
                cache.erase(k);//从cache中删除k
            }
            //将新的key加入链表
            //看有没有频次为1的链表
            auto it = cnt2key.begin();
            if(it == cnt2key.end() || it->first != 1){
                cnt2key.push_front(make_pair(1,list<int>()));//没有频次为1的链表则创建之
                it = cnt2key.begin();
            }
            it->second.push_front(key);//key加入频次为1的链表
            node newnode;
            newnode.value = value;
            newnode.cnt = 1;
            newnode.cnt_pos = it;
            newnode.key_pos = it->second.begin();
            cache[key] = newnode;
        }
    }

    void update(int key, node& n){
        n.cnt++;
        auto nextFreq = next(n.cnt_pos);
        if(nextFreq == cnt2key.end() || nextFreq->first != n.cnt){ //频率为cnt+1的链表不存在
            nextFreq = cnt2key.insert(nextFreq, make_pair(n.cnt, list<int>()));
        }
        n.cnt_pos->second.erase(n.key_pos);//将node从频次为cnt的链表中删除
        if(n.cnt_pos->second.empty()){ //如果删除之后频次为cnt的链表变为空
            cnt2key.erase(n.cnt_pos);//将频次为cnt的链表从cnt2key中删除
        }
        n.cnt_pos = nextFreq;
        nextFreq->second.push_front(key);//将key放入频次为cnt+1的链表的头部
        n.key_pos = nextFreq->second.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);
 */
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章