根据最近最少使用的含义,需要维护一个链表,每次读或者写数据时,需要把数据挂到链表的开头,表示这个数据最近被使用,当链表的长度超过数据的容量时,需要删除链表尾节点。
由于需要在O(1)的时间读取数据,所以需要用到HashMap
由于链表的删除操作也要在O(1)的时间完成,所以需要用双向链表。
因此这道题目就是采用HashMap加双向链表的方法实现。
如果不手写双向链表,可以利用语言特性解决,比如C++可以利用list容器自带的双向链表,hashmap映射的时候使用迭代器。
Java中有LinkListHashMap
Python中有OrderedDict
class LRUCache {
list<pair<int,int>> _list; // 一个双向链表,每个位置存key和value
unordered_map<int,list<pair<int,int>>::iterator> _hashmap; // 一个hashmap,key是key, value是链表的一个节点
int _capacity;
int _size;
public:
LRUCache(int capacity) {
_capacity = capacity;
_size = 0;
}
// O(1)时间读取,hashmap
int get(int key) {
if(!_hashmap.count(key)) return -1;
auto node = _hashmap[key];
int value = node->second;
_list.erase(node); // 删除读取的节点,并将放到链表开头
_list.push_front(make_pair(key,value)); // 双向链表删除是O(1)
_hashmap[key] = _list.begin(); // 跟新hash表
return value;
}
// O(1)时间读取,hashmap,当缓存容量达到上限时,删除最久没有使用的数据,链表
void put(int key, int value) {
if(_hashmap.count(key)){
auto node = _hashmap[key];
_hashmap.erase(key);
_list.erase(node);
_size--;
}
_list.push_front(make_pair(key,value));
_hashmap[key] = _list.begin();
_size++;
if(_size>_capacity){
int key = _list.back().first;
_hashmap.erase(key);
_list.pop_back();
_size--;
}
}
};
面试中遇到还是推荐手写双向链表, 因为特定实现需要去背API, 写双向链表的时候,头和尾都用虚拟节点,这样不会出问题。
struct DLinkedNode {
int key, value;
DLinkedNode* prev;
DLinkedNode* next;
DLinkedNode(): key(0), value(0), prev(nullptr), next(nullptr) {}
DLinkedNode(int _key, int _value): key(_key), value(_value), prev(nullptr), next(nullptr) {}
};
class LRUCache {
private:
DLinkedNode* head;
DLinkedNode* tail;
int size;
int capacity;
unordered_map<int,DLinkedNode*> cache;
public:
LRUCache(int capacity):capacity(capacity),size(0) {
head = new DLinkedNode();
tail = new DLinkedNode();
head->next = tail;
tail->prev = head;
}
int get(int key) {
if(!cache.count(key)) return -1;
auto node = cache[key];
moveTohead(node);
return node->value;
}
void put(int key, int value) {
if(cache.count(key)){
auto node = cache[key];
node->value = value;
moveTohead(node);
return;
}
auto node = new DLinkedNode(key,value);
addToHead(node);
cache[key] = node;
size++;
if(size>capacity) {
auto node = removeTail();
cache.erase(node->key);
size--;
delete node;
}
}
void moveTohead(DLinkedNode* node){
removeNode(node);
addToHead(node);
}
void addToHead(DLinkedNode* node){
node->next = head->next;
node->prev = head;
head->next->prev = node;
head->next = node;
}
void removeNode(DLinkedNode* node){
node->prev->next = node->next;
node->next->prev = node->prev;
}
DLinkedNode* removeTail(){
auto node = tail->prev;
removeNode(node);
return node;
}
};