解题思路
1.每个频率对应一个双向链表,记录对应的key值,如果key对应频率有变化就从此链表中删除
2.链表采用头插法,那么尾部数据自然就是最早插入的数据,优先删除
3.为了方便删除,采用双向链表
4.每插入一个数据,就在hash表中记录key对应链表节点位置,方便查找
代码
class Node {
public:
int key;
int value;
Node* next;
Node* pre;
Node(int mkey = 0, int mvalue = 0) :key(mkey), value(mvalue), next(nullptr), pre(nullptr) {};
};
class DoubleList
{
private:
Node* head; //伪头部
Node* tail; //伪尾部
int Size;
public:
DoubleList() :Size(0)
{
head = new Node;
tail = new Node;
head->next = tail;
tail->pre = head;
};
//头部插入
void push_front(Node* node)
{
node->next = head->next;
head->next->pre = node;
head->next = node;
node->pre = head;
Size++;
}
//删除节点
void remove(Node* node)
{
node->pre->next = node->next;
node->next->pre = node->pre;
Size--;
}
//删除尾部节点
Node* pop_tail()
{
Node* temp = tail->pre; //尾部节点
temp->pre->next = tail;
tail->pre = temp->pre;
Size--;
return temp;
}
Node* front() //返回链表头部节点
{
return head->next;
}
Node* tailNode() //返回链表尾部节点
{
return tail->pre;
}
bool isEmpty()
{
return Size == 0;
}
};
class LFUCache {
public:
unordered_map<int, int> keyToFre;
unordered_map<int, Node*> keyToNode; //利用hash表快速找到下面列表中的节点位置
unordered_map<int, DoubleList> FreToKeyList; //每个频率对应一个key节点列表
int maxCap;
int minFre;
LFUCache(int capacity) {
maxCap = capacity;
minFre = 0;
}
int get(int key) {
if (keyToNode.count(key))
{
increaseFre(key); //更新频率
return keyToNode[key]->value;
}
return -1;
}
void put(int key, int value) {
if(maxCap == 0)
return;
if (keyToNode.count(key))
{
keyToNode[key]->value = value;
increaseFre(key);
return;
}
else //新增资源
{
if (maxCap == keyToFre.size())
{
delMinFre();
}
keyToFre[key] = 1;
Node* newNode = new Node(key, value);
keyToNode[key] = newNode;
FreToKeyList[1].push_front(newNode);
minFre = 1;
}
}
//删除最小使用频率的资源
void delMinFre()
{
Node* temp = FreToKeyList[minFre].pop_tail(); //最开始加入的必然是时间最久的,位于链表尾部
keyToNode.erase(temp->key);
keyToFre.erase(temp->key);
delete temp;
if (FreToKeyList[minFre].isEmpty()) //此使用频率对应的资源列表已经清空,应该删除并更新最小频率
{
FreToKeyList.erase(minFre);
minFre = 1; //删除资源后必有新的资源加入,最低频率更新为1
}
}
//增加原有资源的频率
void increaseFre(int key)
{
int fre = keyToFre[key];
Node* temp = keyToNode[key];
FreToKeyList[fre].remove(temp); //从原频率对应列表删除
if (FreToKeyList[fre].isEmpty()) //此使用频率对应的资源列表已经清空,应该删除并更新最小频率
{
FreToKeyList.erase(fre);
if (fre == minFre)
minFre++; //最低频率更新
}
FreToKeyList[fre + 1].push_front(temp); //节点放入新链表头部
keyToFre[key]++; //key对应频率增加
}
};