之前LRU最近最久未使用缓存结构设计
采取存储方式为
- map<key,List<pair<int,int>>::iterator> 存放key到(key,value)映射 可直接get到
- List<pair<key,value>> 存放(key,value)链表 维护大小为k 末尾为最久未使用
题目描述
一个缓存结构需要实现如下功能。
- set(key, value):将记录(key, value)插入该结构
- get(key):返回key对应的value值
但是缓存结构中最多放K条记录,如果新的第K+1条记录要加入,就需要根据策略删掉一条记录,然后才能把新记录加入。这个策略为:在缓存结构的K条记录中,哪一个key从进入缓存结构的时刻开始,被调用set或者get的次数最少,就删掉这个key的记录;
如果调用次数最少的key有多个,上次调用发生最早的key被删除
这就是LFU缓存替换算法。实现这个结构,K作为参数给出
[要求]
set和get方法的时间复杂度为O(1)
若opt=1,接下来两个整数x, y,表示set(x, y)
若opt=2,接下来一个整数x,表示get(x),若x未出现过或已被移除,则返回-1
对于每个操作2,输出一个答案
示例1
输入
复制
[[1,1,1],[1,2,2],[1,3,2],[1,2,4],[1,3,5],[2,2],[1,4,4],[2,1]],3
输出
复制
[4,-1]
说明
在执行"1 2 4"后,"1 1 1"被删除。因此第二次询问的答案为-1
基本设计结构:
即 :
通过一个min_num记录当前最少set get操作次数
核心map 存放 key到对应(key,value,num)的映射 其中num表示其次数
由于相同的操作数目可能多个(key,value) 故采取拉链法处理map冲突 list链表最后一个表示当前最久的(key,value)
int capacity;//缓存容量
int min_num;//最小的set get操作数
map<int,list<node>::iterator> mp;//存放key对应的(key,value,num)所在的链表位置
map<int,list<node>> num_node;//存放num次操作数目对应的 (key,value,num)键值对
核心get方法:
关键点:
通过map找到对应的(key,value,num)
删除拉链 对应节点
int get(int key)
{
if(capacity==0)
return -1;
if(mp.find(key)==mp.end())
return -1;//没有该元素
int value=mp[key]->value;//获取到value
int num=mp[key]->num;
//同时由于get 将增加get数目
num_node[num].erase(mp[key]);//删除该key-value
if(num_node[num].size()==0) //若删除后为 该拉链Node数量为0
{
num_node.erase(num);
if(num==min_num) //如果删除的是最后一个最小值 那么最小值+1
min_num++;
}
//放入num+1 拉链处
num_node[num+1].push_front(node(key,value,num+1));
mp[key]=num_node[num+1].begin();
return value;
}
核心set方法:
void set(int key,int value)
{
if(capacity==0)
return;
if(mp.find(key)==mp.end()) //之前缓存中不存在
{
if(mp.size()==capacity) //已经满了 //那么需要删除
{
mp.erase(num_node[min_num].back().key);//首先删除在map中的对应的key
num_node[min_num].pop_back();//list末尾删除该key
if(num_node[min_num].size()==0) //如果删除后该被删除
{
num_node.erase(min_num);
}
}
//新加入到拉链[1]位置
num_node[1].push_front(node(key,value,1));
mp[key]=num_node[1].begin();
min_num=1;//最少set get次数一定是1
}
else //之前缓存中存在 那么更新key对应的num数量
{
int num=mp[key]->num;//该(key,value)之前的操作数量
num_node[num].erase(mp[key]);
//在拉链处[num]删除该节点
if(num_node[num].size()==0) //被删光了
{
num_node.erase(num);
if(num==min_num)
min_num++; //如果删光的是最少的那一条 那么最少值增加
}
//同时增加到 [num+1]拉链处 头部
num_node[num+1].push_front(node(key,value,num+1));
mp[key]=num_node[num+1].begin();
}
}
完整代码:
struct node
{
int key,value,num;
node(int k,int v,int n)
{
key=k;
value=v;
num=n;
}
};
class LFU_Cache
{
private:
int capacity;//缓存容量
int min_num;//最小的set get操作数
map<int,list<node>::iterator> mp;//存放key对应的(key,value,num)所在的链表位置
map<int,list<node>> num_node;//存放num次操作数目对应的 (key,value,num)键值对
//方便 获取对应操作数目的 键值对 如num_node[1] 将快速定位到只进行一次set get操作的(key,value)
public:
LFU_Cache(int k)
{
capacity=k; //构造函数
min_num=0;
}
void set(int key,int value)
{
if(capacity==0)
return;
if(mp.find(key)==mp.end()) //之前缓存中不存在
{
if(mp.size()==capacity) //已经满了 //那么需要删除
{
mp.erase(num_node[min_num].back().key);//首先删除在map中的对应的key
num_node[min_num].pop_back();//list末尾删除该key
if(num_node[min_num].size()==0) //如果删除后该被删除
{
num_node.erase(min_num);
}
}
//新加入到拉链[1]位置
num_node[1].push_front(node(key,value,1));
mp[key]=num_node[1].begin();
min_num=1;//最少set get次数一定是1
}
else //之前缓存中存在 那么更新key对应的num数量
{
int num=mp[key]->num;//该(key,value)之前的操作数量
num_node[num].erase(mp[key]);
//在拉链处[num]删除该节点
if(num_node[num].size()==0) //被删光了
{
num_node.erase(num);
if(num==min_num)
min_num++; //如果删光的是最少的那一条 那么最少值增加
}
//同时增加到 [num+1]拉链处 头部
num_node[num+1].push_front(node(key,value,num+1));
mp[key]=num_node[num+1].begin();
}
}
int get(int key)
{
if(capacity==0)
return -1;
if(mp.find(key)==mp.end())
return -1;//没有该元素
int value=mp[key]->value;//获取到value
int num=mp[key]->num;
//同时由于get 将增加get数目
num_node[num].erase(mp[key]);//删除该key-value
if(num_node[num].size()==0) //若删除后为 该拉链Node数量为0
{
num_node.erase(num);
if(num==min_num) //如果删除的是最后一个最小值 那么最小值+1
min_num++;
}
//放入num+1 拉链处
num_node[num+1].push_front(node(key,value,num+1));
mp[key]=num_node[num+1].begin();
return value;
}
};
class Solution {
public:
/**
* lfu design
* @param operators int整型vector<vector<>> ops
* @param k int整型 the k
* @return int整型vector
*/
vector<int> LFU(vector<vector<int> >& operators, int k) {
// write code here
vector<int> res;
LFU_Cache cache(k);
for(int i=0;i<operators.size();i++)
{
if(operators[i].size()==3 && operators[i][0]==1)
{
cache.set(operators[i][1], operators[i][2]);
}
else if(operators[i].size()==2 && operators[i][0]==2)
{
res.push_back(cache.get(operators[i][1]));
}
}
return res;
}
};