1. 官方链接:
2. 题目:
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现
LRUCache
类:
LRUCache(int capacity)
以 正整数 作为容量capacity
初始化 LRU 缓存int get(int key)
如果关键字key
存在于缓存中,则返回关键字的值,否则返回-1
。void put(int key, int value)
如果关键字key
已经存在,则变更其数据值value
;如果不存在,则向缓存中插入该组key-value
。如果插入操作导致关键字数量超过capacity
,则应该 逐出 最久未使用的关键字。函数
get
和put
必须以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
题目给的代码。实现下面三个方法,new, add, get
class LRUCache {
public LRUCache(int capacity) {
}
public int get(int key) {
}
public void put(int key, int value) {
}
}
/**
* 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);
*/
3. 继承LinkedHashMap 实现, 妙!:
author: jeromememory
还是双向链表这一套,只是LinkedHashMap 内部已经写好了方法,来维护链表节点数据!继承,重写,完事了!
package com.nami.algorithm.study.lru;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 描述: 妙啊, 直接继承linkedHashMap 实现了LRU 比双向链表加hashMap简单。
* 重要的点 初始化构造方法时 将accessOrder改为true 并修改removeEldestEntry 的判断逻辑
*
* @Author: lbc
* @Date: 2024-03-26 9:00
* @email: 594599620@qq.com
* @Description: keep coding
*/
public class LRUCache extends LinkedHashMap<Integer, Integer> {
/**
* lru容量
*/
private int capacity;
public LRUCache(int capacity) {
// accessOrder = true 重要
// false, true 确定是按插入顺序还是读取顺序排序
super(capacity, 0.75f, true);
this.capacity = capacity;
}
public int get(int key) {
return super.getOrDefault(key, -1);
}
public void put(int key, int value) {
super.put(key, value);
}
/**
* 重写此方法 重要
*
* @param eldest
* @return
*/
@Override
protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
return size() > capacity;
}
public static void main(String[] args) {
LRUCache cache = new LRUCache(2);
cache.put(1, 2);
cache.put(4, 6);
cache.put(2, 3);
System.out.println(cache.get(4));
System.out.println(cache.get(2));
System.out.println(cache.get(1));
}
}
4. 另外一种: 双向链表+hashMap 实现:
- 哈希表:用于存储 key 和对应的Node节点位置。
- 双向链表:用于存储节点数据,按照访问时间排序。
当访问一个节点时,如果节点存在,我们将其从原来的位置删除,并重新插入到链表头部。这样就能保证链表尾部存储的就是最近最久未使用的节点,当节点数量大于缓存最大空间时就淘汰链表尾部的节点。
当插入一个节点时,如果节点存在,我们将其从原来的位置删除,并重新插入到链表头部。如果不存在,我们首先检查缓存是否已满,如果已满,则删除链表尾部的节点,将新的节点插入链表头部。
package com.nami.algorithm.study.lru;
import java.util.HashMap;
import java.util.Map;
/**
* 描述: 手撸lru
*
* @Author: lbc
* @Date: 2024-03-26 9:31
* @email: 594599620@qq.com
* @Description: keep coding
*/
public class LRUCache2 {
/**
* lru容量
*/
private int capacity;
// 可以使用map 的size()方法,减少维护此变量
// private int currentSize;
private Node head, tail;
// 可将这个Integer 换成别的类型
private Map<Integer, Node> map;
{
// 初始化双向链表和缓存
this.head = new Node();
this.tail = new Node();
head.next = tail;
tail.prev = head;
// this.currentSize = 0;
map = new HashMap<>();
}
public LRUCache2(int capacity) {
this.capacity = capacity;
}
public int get(int key) {
Node node = map.get(key);
if (node == null) {
return -1;
}
moveToHead(node);
return node.value;
}
public void put(int key, int value) {
Node node = map.get(key);
if (node == null) {
Node newNode = new Node(key, value);
map.put(key, newNode);
addToHead(newNode);
// ++this.currentSize;
// System.out.println(map.size() + "," + capacity);
if (map.size() > capacity) {
// 如果超出容量 删除双向链表的尾部节点
Node tail = removeTail();
map.remove(tail.key);
// --this.currentSize;
}
return;
}
// 如果key存在, 先通过哈希表定位,再修改value, 并移到头部
node.value = value;
moveToHead(node);
}
private static class Node {
private int key, value;
private Node prev, next;
public Node() {
}
public Node(int key, int value) {
this.key = key;
this.value = value;
}
}
private void moveToHead(Node node) {
removeNode(node);
addToHead(node);
}
private void addToHead(Node node) {
node.prev = head;
node.next = head.next;
head.next.prev = node;
head.next = node;
}
private void removeNode(Node node) {
node.prev.next = node.next;
node.next.prev = node.prev;
}
private Node removeTail() {
Node prev = tail.prev;
removeNode(prev);
return prev;
}
}
5. 还是考的双向链表的问题,不然hashMap 不也的重写一个吗?
6. mysql 的 buffer pool 有个变种lru,
redis 内存满了的策略中,也有用到lru