LRU缓存机制(最近最久未使用)
前言:
因为面试要求当场撸LFU缓存结构设计题面试的时候没有写出来,所以这里做一个缓存算法集合。😭
1.FIFO(先进先出队列)
2.LRU(最近最久未使用)
3.LFU(最近最少使用)
题目:
leetcode_LRU
牛客_LRU
运用你所掌握的数据结构,设计和实现一个 LRU (最近最久未使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。
获取数据 get(key) - 如果关键字 (key) 存在于缓存中,则获取关键字的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字/值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
要求:
在 O(1) 时间复杂度内完成这两种操作?
示例:
LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );
cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // 返回 1
cache.put(3, 3); // 该操作会使得关键字 2 作废
cache.get(2); // 返回 -1 (未找到)
cache.put(4, 4); // 该操作会使得关键字 1 作废
cache.get(1); // 返回 -1 (未找到)
cache.get(3); // 返回 3
cache.get(4); // 返回 4
解题思路:
最近最久未使用要用O(1)的复杂度去进行set和get操作,最先想到的就是用Hash表+双向链表的形式,双向链表维护一个最近使用的顺序,Hash用来定位。
双向链表:链表的首部.next表示最久未使用的,尾部.pre为最近刚刚使用的。
每次都把最近使用的放在链表尾部,若缓存满了需要移除一个元素的话,移除链表首部.next的元素即可。
代码
class LRUCache {
//双向链表
class DLinked{
int key;
int value;
DLinked pre;
DLinked next;
DLinked(){}
DLinked(int key,int value){this.key=key;this.value=value;}
}
//允许缓存的大小
int capacity;
//主要起定位作用
Map<Integer,DLinked> map;
//链表头部和尾部
DLinked head;
DLinked tail;
//初始化
public LRUCache(int capacity) {
this.capacity=capacity;
map= new HashMap<>();
head=new DLinked();
tail=new DLinked();
head.next=tail;
tail.pre=head;
}
public int get(int key) {
//如果该元素存在
DLinked d = map.get(key);
if(d==null)
{
//如果不存在
return -1;
}
//移除元素当前在链表中的位置
Remove(d);
//将元素添加到链表尾部
Add(d);
return d.value;
}
public void put(int key, int value) {
DLinked exist = map.get(key);
//如果该元素存在
if(exist!=null){
//更新value值
exist.value=value;
//移除元素当前在链表中的位置
Remove(exist);
//将元素添加到链表尾部
Add(exist);
}else{
//该元素不存在
DLinked d = new DLinked(key,value);
//缓存已经满了
if(capacity==map.size()){
//删掉链表头部的元素
map.remove(head.next.key);
Remove(head.next);
}
map.put(key,d);
Add(d);
}
}
//添加到链表尾部pre
public void Add(DLinked d){
d.next=tail;
d.pre=tail.pre;
tail.pre.next=d;
tail.pre=d;
}
//从链表中移除
public void Remove(DLinked d){
d.pre.next=d.next;
d.next.pre=d.pre;
}
}