题目
LRU缓存是什么:LRU缓存机制,你想知道的这里都有
实现 LRU 缓存算法
方法一:直接继承LinkedHashMap调用api
class LRUCache extends LinkedHashMap<Integer, Integer>{
private int capacity;
public LRUCache(int capacity) {
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);
}
@Override
protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
return size() > capacity;
}
}
方法二:自定义LinkedHashMap = HashMap + ListNode == LinkedHashMap
map —>key存 integer value存链表节点
ListNode 存 next 和prev 引用 以及 存 key 和value 具体值
图解:官方图解
步骤:文章来源:https://www.toymoban.com/news/detail-688066.html
- 定义一个自定义的双向链表节点类 DLinkedNode,该类包含 key 和 value 字段,并且具有 prev 和 next 指针用于构建双向链表。
- 创建一个哈希表 cache 用于存储键值对,其中键为索引,值为双向链表的节点。
- 定义变量 size 表示当前哈希表中已存储的键值对数量。
- 定义变量 capacity 表示哈希表的容量。
- 创建伪头部节点 head 和伪尾部节点 tail,并将它们连接起来形成一个空的双向链表。
方法列表:文章来源地址https://www.toymoban.com/news/detail-688066.html
- 构造函数 LRUCache(int capacity):初始化缓存的容量,同时初始化 size、capacity、head 和 tail 变量。
- int get(int key):获取指定键对应的值,如果键不存在则返回 -1。如果键存在,则通过哈希表定位到双向链表中的节点,并将该节点移到链表头部,然后返回节点的值。
- void put(int key, int value):插入或更新键值对。如果键已存在,则更新对应的值,并将对应的节点移到链表头部。如果键不存在,则创建一个新的节点,添加到哈希表和链表头部。如果插入后超出容量,则删除尾部节点,并从哈希表中移除对应的记录。
- DLinkedNode removeTail():在超出容量时删除尾部节点,并返回删除的尾部节点。
- void addToHead(DLinkedNode newNode):将指定节点添加到伪头部节点的后面。
- void moveToHead(DLinkedNode node):将指定节点移到伪头部节点的后面,即删除原位置的节点并将其添加到伪头部节点后面。
- void removeNode(DLinkedNode node):从双向链表中删除指定节点。
class LRUCache {
/**
* 自定义双向链表
*/
class DLinkedNode {
int key;
int value;
DLinkedNode prev;
DLinkedNode next;
public DLinkedNode() {}
public DLinkedNode(int _key, int _value) {key = _key; value = _value;}
}
private Map<Integer, DLinkedNode> cache = new HashMap<Integer, DLinkedNode>();//定义哈希表 key为索引 value为双向链表的节点
private int size;//map的存值后的大小
private int capacity;//map 的容量
private DLinkedNode head, tail; //定义双向链表的伪头部和伪尾部节点
/**
* 定义初始容量方法
* @param capacity
*/
public LRUCache(int capacity) { //定义初始容量方法
this.size = 0;
this.capacity = capacity;
// 使用伪头部和伪尾部节点
head = new DLinkedNode();
tail = new DLinkedNode();
head.next = tail;
tail.prev = head;
}
/**
* 依据key获取对应value
* @param key
* @return
*/
public int get(int key) {
DLinkedNode node = cache.get(key);//获取key对应的链表节点
if (node == null) { //如果node为null 说明key没有对应的value值
return -1;
}
// 如果 key 存在,先通过哈希表定位,再移到头部
moveToHead(node);//将待get的节点移动到头部,再返回节点的值
return node.value;
}
/**
* 往哈希表中添加元素的方法
* @param key
* @param value
*/
public void put(int key, int value) {
DLinkedNode node = cache.get(key);//put时 先获取这个key的位置有没有值 有?覆盖原值 没有就插入进去
if (node == null) {// 如果 key 不存在,创建一个新的节点
DLinkedNode newNode = new DLinkedNode(key, value);
// 添加进哈希表
cache.put(key, newNode);
// 添加至双向链表的头部
addToHead(newNode);
size++; //map集合元素+1
if (size > capacity) {
// 如果超出容量,删除双向链表的尾部节点
DLinkedNode tail = removeTail(); //从链表中把尾结点删除,并且方法得返回这个节点 方便map把这个节点对应的元素remove掉
cache.remove(tail.key);
size--;
}
}else{// 如果 key 存在,覆盖原值,并移到链表头部
node.value = value;
moveToHead(node);
}
}
/**
* 超出容量时删除尾部节点 并且返回尾部节点 方便对清除map中的残留
* @return
*/
private DLinkedNode removeTail() {
DLinkedNode tailNode = tail.prev;//获取伪尾节点的前一个节点 即链表真实的尾节点
removeNode(tailNode);
return tailNode;//返回尾部节点 方便对清除map中的残留
}
/**
* 新增节点时节点添加到伪头结点后面的位置
* @param newNode
*/
private void addToHead(DLinkedNode newNode) {
newNode.prev = head;
newNode.next = head.next;
head.next.prev = newNode;
head.next = newNode;
}
/**
* get方法获取key对应value
* put方法出现重复key时 覆盖原value
* 这两种情况都会触发将操作的节点移动到伪首节点的后面
* @param node
*/
private void moveToHead(DLinkedNode node) {
//删除原位置的自己
removeNode(node);
//将自己添加到伪首节点的后面
addToHead(node);//调用上面写过的将节点添加到伪首节点的后面
}
/**
* 删除node节点
* @param node
*/
private void removeNode(DLinkedNode node) {
node.prev.next = node.next;
node.next.prev = node.prev;
node.next = null;
node.prev = null;
}
}
到了这里,关于【LeetCode-中等题】146. LRU 缓存的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!