先来看LRU Cache,LRU Cache的重点就是建立一个 map<key, list<>::iterator> 的hash table。key存需要找的key,value是list的iterator。
而这个list<pair<int, int>> 存的就是key value pair。在get或set时,要把这个<key, value> pair 从当前位置,放到栈顶。这个是用list.splice函数实现的。splice函数的用法如下:
list.splice(position, list name, list iterator)
lst.splice(lst.begin(), lst, it);
注意:在这里统一一下,无论LRU还是LFU Cache, 一律先删,再加。
class LRUCache {
public:
LRUCache(int capacity) {
this->capacity = capacity;
}
int get(int key) {
if(!mp.count(key)) return -1;
auto it = mp[key];
lst.splice(lst.begin(), lst, it);
mp[key] = lst.begin();
return it->second;
}
void put(int key, int value) {
if(get(key) != -1){
mp[key]->second = value;
}
else{
if(lst.size() == capacity){
auto it = lst.back();
lst.pop_back();
mp.erase(it.first);
}
lst.push_front({key, value});
mp[key] = lst.begin();
}
}
private:
int capacity;
list<pair<int, int>> lst;
unordered_map<int, list<pair<int, int>>::iterator> mp;
};
LFU Cache则要复杂不少,具体参考grandyang的LFU solution:
http://www.cnblogs.com/grandyang/p/6258459.html
LFU Cache的经典solution是用doubly linkded list (http://dhruvbird.com/lfu.pdf),上文解法用三个Hashmap模拟了这个doubly linked list,非常巧妙. 另外trick是在set时调用get,并讨论考虑get != -1的情况. 本文变化是当capacity reach full时,将队尾pop出去. 注意,第一个keyValue Hashmap: key => {value, frequency}
纯O(1)的solution:
class LFUCache {
public:
LFUCache(int capacity) {
cap = capacity;
}
int get(int key) {
if(!keyValue.count(key)) return -1;
auto it = locator[key];
frequency[keyValue[key].second++].erase(it);
frequency[keyValue[key].second].push_front(key);
locator[key] = frequency[keyValue[key].second].begin();
if(frequency[minFreq].empty()) minFreq++;
return keyValue[key].first;
}
void put(int key, int value) {
if(cap <= 0) return;
if(get(key) != -1){
keyValue[key].first = value;
}else{
if(keyValue.size() == cap){
int temp = frequency[minFreq].back();
frequency[minFreq].pop_back();
locator.erase(temp);
keyValue.erase(temp);
}
keyValue[key] = {value, 1};
frequency[1].push_front(key);
locator[key] = frequency[1].begin();
minFreq = 1;
}
}
private:
int cap, minFreq;
unordered_map<int, pair<int, int>> keyValue;
unordered_map<int, list<int>> frequency;
unordered_map<int, list<int>::iterator> locator;
};
简单一点的solution,不O(1), 但是省掉了一个HashMap,直接用list.remove(key)
class LFUCache {
public:
LFUCache(int capacity) {
this->capacity = capacity;
}
int get(int key) {
if(!keyValue.count(key)) return -1;
frequency[keyValue[key].second++].remove(key);
frequency[keyValue[key].second].push_front(key);
if(frequency[minFreq].empty()) minFreq++;
return keyValue[key].first;
}
void put(int key, int value) {
if(capacity <= 0) return;
if(get(key) != -1){
keyValue[key].first = value;
}
else{
if(keyValue.size() == capacity){
int temp = frequency[minFreq].back();
frequency[minFreq].pop_back();
keyValue.erase(temp);
}
keyValue[key] = {value, 1};
frequency[1].push_front(key);
minFreq = 1;
}
}
private:
int capacity, minFreq = 0;
unordered_map<int, pair<int, int>> keyValue;
unordered_map<int, list<int>> frequency;
};