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