LRU是内存置换算法中的最近最少使用算法,原理如下:
可以看到LRU的原理很简单,但是实现起来就不简单了。
leetcode 146题要求设计一个插入页面和获取页面都是O(1)的算法 这个其实就不容易了。
如果不考虑效率,直接用单链表实现很简单,事件复杂度get为O(n) put也为O(n)。
为了提高效率,用双向链表+hashMap的方法。
class LRUCache {
//双向链表
class Node{
int val, key;
Node left, right;
Node(){
this.key = 0;
this.val = 0;
left = null;
right = null;
}
}
//存储key对应的node
HashMap<Integer, Node> hm = new HashMap<>();
//头插法
void insert(Node h, Node p){
p.right = h.right;
h.right = p;
p.left = h;
p.right.left = p;
}
void delete(Node p){
p.left.right = p.right;
p.right.left = p.left;
}
Node hfree = new Node();
Node tfree = new Node();
Node huse = new Node();
Node tuse = new Node();
int freeSize;
public LRUCache(int capacity) {
freeSize = capacity;
hfree.right = tfree;
tfree.left = hfree;
huse.right = tuse;
tuse.left = huse;
for(int i=0; i<capacity; i++){
Node node = new Node();
insert(hfree, node);
}
}
//如果hash表中有 查到对应node 返回值 更新双向链表把这个node调整到最左边
public int get(int key) {
if(hm.containsKey(key)){
Node node = hm.get(key);
delete(node);
insert(huse, node);
return node.val;
}
else return -1;
}
public void put(int key, int value) {
if(hm.containsKey(key)){ //key存在 修改值 调整链表即可
Node node = hm.get(key);
delete(node);
insert(huse, node);
node.val = value;
}else{ //不存在 需要考虑两种情况 没有空闲空间和有空闲空间
if(freeSize == 0){ //删除最近最久未使用页
Node node = tuse.left;
hm.remove(node.key);
delete(node);
freeSize++;
insert(hfree, node); //把这个node查到空闲块
}
freeSize--;
Node node = hfree.right; //从空闲块里拿出一个块
delete(node);
node.key = key;
node.val = value;
hm.put(key, node);
insert(huse, node);
}
}
}
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/
这里保存两个链表主要是为了不反复创建Node,并且,可以不用记录freeSize直接通过hfree和tfree来查看时候还有空闲空闲块即可。