LRU算法
LRU算法
LRU算法即最近最少使用算法(Least Recently Used),是一种常用的缓存淘汰策略。其基本思想是,如果一个数据在最近一段时间内没有被访问到,那么它在未来也不太可能被访问到,因此可以将其从缓存中淘汰掉。
LRU算法的实现通常采用哈希表辅以双向链表来完成。具体来说,哈希表用于快速定位缓存中的元素,而双向链表则用于维护缓存中元素的访问顺序。当需要淘汰缓存中的元素时,我们只需要将双向链表尾部的节点删除即可。
LRU算法的例子
以下是一个简单的Java实现LRU算法的例子:
import java.util.*;
public class LRUCache<K, V> {
private Map<K, Node> map;
private int capacity;
private Node head;
private Node tail;
private class Node {
K key;
V value;
Node prev;
Node next;
public Node(K key, V value) {
this.key = key;
this.value = value;
}
}
public LRUCache(int capacity) {
this.capacity = capacity;
map = new HashMap<>(capacity);
head = new Node(null, null);
tail = new Node(null, null);
head.next = tail;
tail.prev = head;
}
public V get(K key) {
Node node = map.get(key);
if (node == null) {
return null;
}
moveToHead(node);
return node.value;
}
public void put(K key, V value) {
Node node = map.get(key);
if (node == null) {
node = new Node(key, value);
map.put(key, node);
addToHead(node);
if (map.size() > capacity) {
Node tail = removeTail();
map.remove(tail.key);
}
} else {
node.value = value;
moveToHead(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 void moveToHead(Node node) {
removeNode(node);
addToHead(node);
}
private Node removeTail() {
Node node = tail.prev;
removeNode(node);
return node;
}
}
LRU算法的具体步骤:
- 定义缓存大小和缓存结构:首先需要定义缓存的大小和存储结构。通常使用哈希表加双向链表的结构实现LRU算法。
- 查找缓存:当需要查找某个数据时,先在哈希表中查找是否存在该数据。如果存在,则将其移动到双向链表的头部,表示该数据最近被访问过;如果不存在,则说明缓存中没有该数据,需要从磁盘或其他来源加载数据,并插入到双向链表的头部,同时更新哈希表中的映射关系。
- 写入缓存:当需要写入数据时,先查找哈希表中是否存在该数据。如果存在,则更新该数据的值,并将其移动到双向链表的头部;如果不存在,则插入到双向链表的头部,并更新哈希表中的映射关系。如果缓存已满,则删除双向链表尾部的数据,同时更新哈希表中的映射关系。
LRU算法的时间复杂度为O(1),因为每次访问或操作都是在哈希表和双向链表中进行,而不需要遍历整个缓存。
代码解释
Java代码实现了LRU缓存算法。以下是每个方法的意图:
- 构造函数:初始化LRU缓存的容量,创建一个HashMap用于存储缓存中的数据以及它们在双向链表中的节点,并初始化头结点和尾节点。
- get方法:根据key查找缓存中的数据,如果不存在则返回null,否则将对应的节点移动到双向链表的头部,并返回节点的值。
- put方法:向缓存中添加数据。如果key已经存在,则更新对应节点的value并将其移到链表头部;如果key不存在,则创建新的节点并将其添加到链表头部,并更新HashMap中的映射关系。如果缓存大小超过限制,则删除双向链表尾部的节点并从HashMap中删除对应的映射关系。
- addToHead方法:将节点添加到双向链表头部。
- removeNode方法:从双向链表中删除指定节点。
- moveToHead方法:将指定节点移动到链表头部。
- removeTail方法:删除双向链表尾部的节点,并返回该节点