LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法
选择最近最久未使用的数据予以淘汰。
除此之外,在redis缓存中也是用到了这种算法。redis有读写两个操作,然后缓存是有空间限制的,大小会有一定上线的,
来自https://leetcode-cn.com/problems/lru-cache/
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。
实现 LRUCache 类:
LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
进阶:你是否可以在 O(1) 时间复杂度内完成这两种操作?
示例:
输入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]
解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1); // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2); // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1); // 返回 -1 (未找到)
lRUCache.get(3); // 返回 3
lRUCache.get(4); // 返回 4
进阶:你是否可以在 O(1) 时间复杂度内完成这两种操作?
这里要求一次命中,写要写成功,读也要读成功。
思考java中什么样的数据结构能一次找到。
1.所谓缓存,必须要有读+写操作,按照命中率的思路考虑,写操作时间复杂度都需要为O(1)
2.特性要求
2.1.必须要有顺序之分,一区分最近使用的和很久没有使用的数据排序。
2.2.写和读操作一次搞定
2.3.**如果容量(坑位)满了要删除最不常用的数据,每次新访问还要把新的数据插入到队头(**按照业务你自己设定左右那一边是队头)
查找快、插入快、删除快,且还需要先后排序------->什么样的数据结构可以满足这个问题?
你是否可以在O(1)时间复杂度内完成这个操作?
如果一次就可以找到,你觉得什么数据结构最合适??
哈希存储结构
LRU的算法核心是哈希+链表
本质就是HashMap+DoubleLinkedList,时间复杂度O(1),哈希表+双向链表的结合体
哈希能够实现O(1)时间内的查找,双向链表能够保证增删比较快。即查找用哈希,增删用链表。
查看API,可以看到继承自HashMap
public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>
这种数据结构非常适合于构造LRU算法
eldest是最近最少使用的entry插入到map中。
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}
以下是代码实现
public class LRUCacheDemo<K,V> extends LinkedHashMap<K,V>{
private int capacity;
public LRUCacheDemo(int capacity){
//参数1,坑位,传过来的值,参数2,加载因子,参数3,访问顺序
super(capacity,0.75F,true);
this.capacity=capacity;
}
//怎么弹出去一个坑位
@Override
protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) {
return super.size()>capacity;
}
public static void main(String[] args) {
//坑位是3个
LRUCacheDemo lruCacheDemo=new LRUCacheDemo<>(3);
lruCacheDemo.put(1,"a");
lruCacheDemo.put(2,"b");
lruCacheDemo.put(3,"c");
System.out.println(lruCacheDemo.keySet());
lruCacheDemo.put(4,"d");
System.out.println(lruCacheDemo.keySet());
lruCacheDemo.put(3,"c");
System.out.println(lruCacheDemo.keySet());
lruCacheDemo.put(3,"c");
System.out.println(lruCacheDemo.keySet());
lruCacheDemo.put(3,"c");
System.out.println(lruCacheDemo.keySet());
lruCacheDemo.put(3,"c");
System.out.println(lruCacheDemo.keySet());
lruCacheDemo.put(5,"x");
System.out.println(lruCacheDemo.keySet());
}
}