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);
 */
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章