最近最少使用算法,主要思路:
双链表:双链表存储数据,插入和删除元素都很方便,复杂度都是O(1)。
头节点是最近使用的元素,尾节点是最近最少使用,即需要淘汰的节点。
空间满了的话,就淘汰尾节点,新增元素时,放在头节点。
在读写操作时,如果命中缓存,就把该节点从当前位置移植到头节点。
为什么不用数组,因为插入和删除需要平移元素,复杂度是O(n)。
哈希map:
用于查找数据,key是数据的key,value是数据的指针(即数据在双链表中的位置)
最大容量:
如果超过容量,就把尾节点淘汰,新数据添加到头节点。
Java实现
java中双链表用LinkedList数据结构,底层用双链表实现。
class LRUCache {
public class Node {
int key;
int value;
Node(int key, int value) {
this.key = key;
this.value = value;
}
}
/**
* map用于存储key对应的节点
*/
private Map<Integer, Node> map = new HashMap<>();
/**
* 双链表存储,头节点是最近使用的元素,尾节点是最近最少使用
* 空间满了的话,就淘汰尾节点
* 新增元素时,放在头节点
*/
private LinkedList<Node> dList = new LinkedList<>();
private int MAX_SIZE;
public LRUCache(int capacity) {
MAX_SIZE = capacity;
}
/**
* 如果关键字 key 存在于缓存中,则返回关键字的value,否则返回 -1
* @param key
* @return
*/
public int get(int key) {
if (map.containsKey(key)) {
Node data = map.get(key);
dList.remove(data);
dList.addFirst(data);
return data.value;
}
return -1;
}
/**
* 如果关键字 key 已经存在,则变更其数据值 value ;
* 如果不存在,则向缓存中插入该组 key-value 。
* 如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字(即尾节点)
* 新数据插入头节点
* @param key
* @param value
*/
public void put(int key, int value) {
if (map.containsKey(key)) {
Node cache = map.get(key);
cache.value = value;
dList.remove(cache);
dList.addFirst(cache);
return;
}
if (dList.size()+1 > MAX_SIZE) {
Node cache = dList.removeLast();
map.remove(cache.key);
}
Node data = new Node(key, value);
dList.addFirst(data);
map.put(key, data);
}
}
C++实现
std::list是双链表
unordered_map用于查找
struct Node {
int key;
int value;
Node(int key, int value) {
this->key = key;
this->value = value;
}
};
class LRUCache {
public:
int _MAX_SIZE;
std::list<Node*> _dlist;
std::unordered_map<int, std::list<Node*>::iterator> _dmap;
LRUCache(int size) {
this->_MAX_SIZE = size;
}
int get(int key) {
auto it = _dmap.find(key);
if (it != _dmap.end()) {
Node* data = move2Front(it->second);
return data->value;
}
return -1;
}
void put(int key, int value) {
auto it = _dmap.find(key);
if (it != _dmap.end()) {
Node* data = move2Front(it->second);
data->value = value;
return;
}
Node* newData = new Node(key, value);
if (_dlist.size() + 1 > _MAX_SIZE) {
Node* deleteNode = _dlist.back();
_dmap.erase(deleteNode->key);
_dlist.pop_back();
delete deleteNode;
}
_dlist.push_front(newData);
_dmap[key] = _dlist.begin();
}
Node* move2Front(std::list<Node*>::iterator it) {
Node* data = *it;
_dlist.erase(it);
_dlist.push_front(data);
_dmap[data->key] = _dlist.begin();
return data;
}
};