文章目录
- [1. LRU](#1. LRU)
- [2. LFU](#2. LFU)
1. LRU
LRU,即least recently used, 即一个具有固定容量的数据结构,其数据淘汰更新策略,根据最近访问,最久未被访问的元素优先被淘汰。
数据结构设计思路:
- 链表。通过链表的尾部删除,头部插入,可以非常完美地模拟淘汰更新策略。
- 哈希表。LRU访问,一般是KV结构,要能够通过key,找到value 。 实际中,哈希表建立的是key 到相应元素在链表中迭代器的映射关系 ,这样不仅可以拿到value,同时可以利用迭代器改变链表元素位置。
- 整型变量。存储容量。
pair类型。存储KV键值对。
cpp
class LRUCache {
public:
LRUCache(int capacity) {
capacity_ = capacity;
}
int get(int key) {
if(hash_.count(key) == 0)
return -1;
auto it = hash_[key];
int val = it->second;
l_.erase(it);
l_.push_front({key,val});
hash_[key] = l_.begin();
return val;
}
void put(int key, int value) {
if(hash_.count(key)) {
hash_[key]->second = value;
(void)get(key);
return;
}
if(capacity_ == 0) {
int key = l_.back().first;
l_.pop_back();
hash_.erase(key);
capacity_++;
}
l_.push_front({key,value});
hash_[key] = l_.begin();
capacity_--;
}
private:
using pii = pair<int,int>;
int capacity_;
list<pii> l_;
unordered_map<int,list<pii>::iterator> hash_;
};
2. LFU
LFU,即 least frequently used, 即最少频次被使用,同样是有一定淘汰更新策略的固定容量的数据结构。
LFU的淘汰更新策略:
- 超出容量时,优先淘汰被使用频次最低的元素。
- 相同频次的元素,优先淘汰最久未被使用的元素。
实际上,如果只看一个频次,那么LFU就是LRU,多个LRU分别对应不同频次,就构成了LFU。
所以,我们如下设计LFU数据结构:
- 哈希表。这个哈希表建立的是频次到具体LRU链表的映射,这样可以通过频次,找到存储相应访问频次元素的链表。
- 哈希表。该哈希表建立的是key到相应元素链表迭代器的映射,这样可以快速定位元素,而无需找到相应链表后,再遍历。
- 整型变量。存储容量。
- 整型变量。存储当前最小频次,这样淘汰时,可以快速通过哈希找到相应链表。
- 元素数据结构。LRU 中,元素数据结构,简单的
pair<int,int>即可,而LFU 中,还需在key和value外添加频次,因为这样通过哈希拿到相应元素迭代器后,才能知道这个元素在哪个频次的链表中,进而才能进行更改元素位置的操作。
cpp
class LFUCache {
public:
LFUCache(int capacity) {
capacity_ = capacity;
min_fre_ = 0;
}
int get(int key) {
if(hash_.count(key) == 0)
return -1;
int val = hash_[key]->val_;
int fre = hash_[key]->fre_;
list<Node>& l = lists_[fre];
l.erase(hash_[key]);
if(l.empty()) {
lists_.erase(fre);
if(min_fre_ == fre)
min_fre_++;
}
lists_[fre + 1].push_front(Node(key,val,fre + 1));
hash_[key] = lists_[fre + 1].begin();
return val;
}
void put(int key, int value) {
if(hash_.count(key)) {
hash_[key]->val_ = value;
(void)get(key);
return;
}
if(capacity_ == 0) {
list<Node>& l = lists_[min_fre_];
int key = l.back().key_;
hash_.erase(key);
l.pop_back();
if(l.empty())
lists_.erase(min_fre_);
capacity_++;
}
lists_[1].push_front(Node(key,value,1));
min_fre_ = 1;
hash_[key] = lists_[1].begin();
capacity_--;
}
private:
struct Node {
int key_;
int val_;
int fre_;
Node(int key,int val,int fre) {
key_ = key;
val_ = val;
fre_ = fre;
}
};
int capacity_;
int min_fre_;
unordered_map<int,list<Node>> lists_;
unordered_map<int,list<Node>::iterator> hash_;
};