LFU缓存结构设计
题目描述
一个缓存结构需要实现如下功能。
- 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)
输入描述:
第一行两个个整数N, K,表示操作数量以及缓存结构大小
接下来N行,第一行一个整数opt表示操作类型。
若opt=1,接下来两个整数x, y,表示set(x, y)
若opt=2,接下来一个整数x,表示get(x),若x未出现过或已被移除,则返回-1
输出描述:
对于每个操作2,输出一个答案
示例1
输入
8 3
1 1 1
1 2 2
1 3 2
1 2 4
1 3 5
2 2
1 4 4
2 1
输出
4
-1
说明
在执行"1 2 4"后,"1 1 1"被删除。因此第二次询问的答案为-1
备注:
1
⩽
K
⩽
N
⩽
1
0
5
1 \leqslant K \leqslant N \leqslant 10^5
1⩽K⩽N⩽105
−
2
∗
1
0
9
⩽
x
,
y
⩽
2
∗
1
0
9
-2 * 10^9 \leqslant x, y \leqslant 2 * 10^9
−2∗109⩽x,y⩽2∗109
题解:
对每个 (key, value) 键值对,使用一个桶保存 key 被操作的次数,这里可以使用 multimap ,其可自动按照次数从小到大排序,定义为:multimap<int, pair<key, value> > freq_map;然后需要额外使用一个 unordered_map ,记录每个关键字对应的是哪个桶,定义为:unordered_map<int, multimap<int, pair<key, value> > > hash_map
-
set(key, value)
首先在 hash_map 中查询是否存 key ,若存在,将其的出现频率+1,重新插入 freq_map 中,删除原有的 freq_map 记录;若不存在,则在 freq_map 中插入一个新的记录,在 hash_map 中记录新插入的位置,同时,若插入后 freq_map 的元素个数大于 k ,则要将以 freq_map.begin() 删除(此处 begin() 指的是 出现频率最小且最早的元素),同时在 hash_map 中删除对应的 key 。
-
get(key)
同样的需要在 hash_map 中查询是否存在该 key ,不存在返回 -1,否则输出对应的值即可
代码:
#include <cstdio>
#include <unordered_map>
#include <map>
using namespace std;
template <class key_t, class value_t>
class LFU {
public:
explicit LFU( size_t size ) : max_size( size ) {}
value_t get( key_t key ) {
auto iter = hash_map.find( key );
if ( iter != hash_map.end() ) {
auto newIter = freq_map.insert( { iter->second->first + 1, iter->second->second } );
freq_map.erase( iter->second );
hash_map[ key ] = newIter;
return iter->second->second.second;
} else {
return -1;
}
}
void set( key_t key, value_t value ) {
auto iter = hash_map.find( key );
if ( iter != hash_map.end() ) {
auto newIter = freq_map.insert( { iter->second->first + 1, iter->second->second } );
newIter->second.second = value;
freq_map.erase( iter->second );
hash_map[key] = newIter;
} else {
auto newIter = freq_map.insert( {1, { key, value } } );
hash_map[key] = newIter;
if ( freq_map.size() > max_size ) {
hash_map.erase( freq_map.begin()->second.first );
freq_map.erase( freq_map.begin() );
}
}
}
private:
using key_value_pair = pair< key_t, value_t >;
using map_iterator = typename multimap< int, key_value_pair >::iterator;
size_t max_size;
multimap< int, key_value_pair > freq_map;
unordered_map< key_t, map_iterator > hash_map;
};
int main(void) {
int n, k;
int op, key, val;
scanf("%d%d", &n, &k);
LFU<int, int> lfu( k );
while ( n-- ) {
scanf("%d%d", &op, &key);
if ( op == 1 ) {
scanf("%d", &val);
lfu.set( key, val );
} else {
printf("%d\n", lfu.get( key ));
}
}
return 0;
}