题目链接: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<Integer,DoubleLinkedList> 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方法加入数据是顺序加入的,即从尾部加入)
代码如下:
/**
* @param {number} capacity
*/
// 最近被访问过的节点在map尾部
// 构造函数
var LRUCache = function(capacity) {
this.capacity=capacity;
// map的set方法是按照顺序插入的(相当于从尾部插入)
this.map=new Map();
};
/**
* @param {number} key
* @return {number}
*/
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;
};
/**
* @param {number} key
* @param {number} value
* @return {void}
*/
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);
}
};