Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get
and set
.
get(key)
- Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
set(key, value)
- Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.
为了寻求速度,基本是hash table,O(1)。怎么维护LRU呢?可以用一个链表,访问过的元素放到末端。链表的插入删除都是O(1)。
class LRUCache{
public:
LRUCache(int capacity) {
this->capacity = capacity;
size = 0;
}
int get(int key) {
int value = -1;
auto iter = cache_map.find(key);
if (iter != cache_map.end()) {
value = iter->second->second;
cache_list.erase(iter->second);
cache_map[key] = cache_list.insert(cache_list.end(), make_pair(key, value));
}
return value;
}
void set(int key, int value) {
auto iter = cache_map.find(key);
if (iter != cache_map.end()) {
cache_list.erase(iter->second);
}
else {
if (size < capacity) {
++size;
}
else {
auto front = cache_list.front();
cache_map.erase(front.first);
cache_list.pop_front();
}
}
cache_map[key] = cache_list.insert(cache_list.end(), make_pair(key, value));
}
private:
int capacity;
int size;
list<pair<int, int> > cache_list; // key-value pair
typedef list<pair<int, int> >::iterator pnode;
unordered_map<int, pnode> cache_map; // key - iterator to list
};
==========================第二次=======================
上面用了list+map来实现,如果要求只用一个Map来实现呢?先后关系是必须要维护的,所以只能在map里面自己维护先后顺序了。这种方式容易出错啊,指针操作,边界条件,一个元素的情况,总之折腾很久。
struct LRUListNode {
int key;
int val;
LRUListNode *next;
LRUListNode *prev;
LRUListNode(int key, int val) {
this->key = key;
this->val = val;
next = NULL;
prev = NULL;
}
};
class LRUCache{
public:
LRUCache(int capacity) {
m_capacity = capacity;
if (m_capacity == 0) {
m_capacity = 10;
}
m_size = 0;
m_oldest = NULL;
m_latest = NULL;
}
int get(int key) {
int val = -1;
auto iter = m_lruMap.find(key);
if (iter != m_lruMap.end()) {
LRUListNode *tmp = iter->second;
val = tmp->val;
// If it is the last one, no need update.
if (tmp != m_latest) {
if (tmp->prev != NULL) {
tmp->prev->next = tmp->next;
}
tmp->next->prev = tmp->prev;
if (m_oldest == tmp) {
m_oldest = tmp->next;
m_oldest->prev = NULL;
}
joinNode(m_latest, tmp);
tmp->next = NULL;
m_latest = tmp;
}
}
return val;
}
void set(int key, int value) {
if (get(key) != -1) {
m_latest->val = value;
}
else {
if (m_size < m_capacity) {
LRUListNode *inserted = new LRUListNode(key, value);
m_lruMap[key] = inserted;
++m_size;
if (m_latest != NULL) {
joinNode(m_latest, inserted);
}
m_latest = inserted;
if (m_oldest == NULL) {
m_oldest = inserted;
}
}
else {
LRUListNode *deleting = m_oldest;
m_lruMap.erase(deleting->key);
deleting->key = key;
deleting->val = value;
m_lruMap[key] = deleting;
// If capacity is only 1, no need update.
if (m_oldest != m_latest) {
m_oldest = m_oldest->next;
m_oldest->prev = NULL;
joinNode(m_latest, deleting);
deleting->next = NULL;
m_latest = deleting;
}
}
}
}
private:
void joinNode(LRUListNode *n1, LRUListNode *n2) {
n1->next = n2;
n2->prev = n1;
}
int m_capacity;
int m_size;
LRUListNode *m_oldest;
LRUListNode *m_latest;
unordered_map<int, LRUListNode*> m_lruMap;
};