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