题目链接:LRUCache
分析:LRU最近最久未使用,可以把每次刚被访问过的节点从缓存区的左端加入,那么缓存区右端就是最近最久未使用的节点,缓存区满了的话就从右端删除。get时,就把节点从原位置移除,再从左端加入缓存区;set时,就把节点从左端加入缓存区,如果缓存区满了,就从右端移除一个节点。
Java实现:哈希表+双向链表:
虽然有linkedHashMap结构,但还是需要自己实现双向链表结构。
代码如下:
//LRU缓存类:双向链表+HashMap(虽然java有自带的linkedHashMap(结合了哈希表与双向链表结构),但还是希望能够自己实现一个双向链表)
//最近被访问过的节点在链表头部
public class LRUCache {
// 内部还有一个类
class DoublelinkedList{
// 链表节点data区
int key;
int value;
// 链表节点指针区
DoublelinkedList prev;
DoublelinkedList next;
// 无参构造函数
private DoublelinkedList(){};
// 构造函数
private DoublelinkedList(int key,int value){
this.key=key;
this.value=value;
}
}
// LRUCache类的数据
private int size;
private int capacity;
private Map cache=new HashMap<>();
// 虚拟头尾节点,(添加和删除操作时不必再判断是否有相邻节点)
private DoublelinkedList head,tail;
// 构造函数(初始化)
public LRUCache(int capacity) {
this.size=0;
this.capacity=capacity;
head=new DoublelinkedList();
tail=new DoublelinkedList();
head.next=tail;
tail.prev=head;
}
// 链表data域中的key和哈希表中的key值是相等的。(方便通过节点查找对应的key-value)
public int get(int key) {
DoublelinkedList node=cache.get(key);
if(node==null)
return -1;
// 把刚访问过的节点移动到链表头部
moveToHead(node);
return node.value;
}
public void put(int key, int value) {
DoublelinkedList node=cache.get(key);
if(node==null){
// 创建新节点
node=new DoublelinkedList(key,value);
// 加入哈希表
cache.put(key,node);
// 加入链表头部
addToHead(node);
// 还要判断当前size
size++;
if(size>capacity){
// 删除链表尾部节点
DoublelinkedList tailNode=removeTail();
// 并从哈希表中删除
cache.remove(tailNode.key);
size--;
}
}
else{
// 更新值
node.value=value;
// 移动到链表头部
moveToHead(node);
}
}
private void addToHead(DoublelinkedList node){
node.next=head.next;
head.next.prev=node;
head.next=node;
node.prev=head;
}
// 包括从当前位置删除和添加到链表头部
private void moveToHead(DoublelinkedList node){
removeNode(node);
addToHead(node);
}
private void removeNode(DoublelinkedList node){
node.prev.next=node.next;
node.next.prev=node.prev;
node.next=null;
node.prev=null;
}
private DoublelinkedList removeTail(){
DoublelinkedList node=tail.prev;
node.prev.next=tail;
tail.prev=node.prev;
return node;
}
}
Javascript实现:Map(Map的set方法加入数据是顺序加入的,即从尾部加入)
代码如下:
// 最近被访问过的节点在map尾部
// 构造函数
var LRUCache = function(capacity) {
this.capacity=capacity;
// map的set方法是按照顺序插入的(相当于从尾部插入)
this.map=new Map();
};
LRUCache.prototype.get = function(key) {
if(this.map.has(key)){
let value=this.map.get(key);
// 删除节点
this.map.delete(key);
// 再从尾部插入节点
this.map.set(key,value);
return value;
}
return -1;
};
LRUCache.prototype.put = function(key, value) {
if(this.map.has(key)){
this.map.delete(key);
}
this.map.set(key,value);
if(this.map.size>this.capacity){
// 删除map中的第一个元素
this.map.delete(this.map.keys().next().value);
}
};



