问题
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。 实现 LRUCache 类:
LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
思路
用Map去存储数据,再加一个双向链表的辅助结构,链表的头部总是最近访问的,当超过容量之后总是从尾部删除节点。需要注意的是:如果连续put相同key的不同value,需要更新value值,而且还要调整该节点到链表的头部。
代码一
可以直接使用Java的LinkedHashMap实现。
public class LRUCache<K, V> extends LinkedHashMap<K, V> {
private final int capacity;
private final LRUCacheListener<K, V> listener;
public LRUCache(int capacity) {
this(capacity, 0.75F, null);
}
public LRUCache(int capacity, LRUCacheListener<K, V> listener) {
this(capacity, 0.75F, listener);
}
public LRUCache(int capacity, float loadFactor, LRUCacheListener<K, V> listener) {
// 由于总是先将数据put进去,然后移除,所以有可能会有扩容操作,capacity+1防止动态扩容。
super((int) Math.ceil(capacity / loadFactor) + 1, loadFactor, true);
this.capacity = capacity;
this.listener = listener;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
boolean remove = size() > capacity;
// 每次put操作都会调用removeEldestEntry,所以在processEldest之前判断容量是否满了。
if (listener != null && remove) {
listener.processEldest(eldest);
}
return remove;
}
}
public interface LRUCacheListener<K, V> {
public void processEldest(Map.Entry<K, V> eldest);
}
代码二
public class LRUCache<K, V> {
private final Map<K, ListNode<K, V>> map;
private final ListNode<K, V> head;
private final ListNode<K, V> tail;
private final int capacity;
public LRUCache(int capacity) {
this.capacity = capacity;
map = new HashMap<>(capacity);
head = new ListNode<>(null, null);
tail = new ListNode<>(null, null);
head.next = tail;
tail.prev = head;
}
public void put(K key, V value) {
ListNode<K, V> node = map.get(key);
if (node != null) {
node.val = value;
// 此处直接通过get方法调整
get(key);
} else {
node = new ListNode<>(key, value);
map.put(key, node);
node.next = head.next;
head.next.prev = node;
head.next = node;
node.prev = head;
if (map.size() > capacity) {
ListNode<K, V> removed = tail.prev;
map.remove(removed.key);
removed.prev.next = tail;
tail.prev = removed.prev;
}
}
}
public V get(K key) {
ListNode<K, V> node = map.get(key);
if (node != null) {
node.prev.next = node.next;
node.next.prev = node.prev;
node.next = head.next;
head.next.prev = node;
head.next = node;
node.prev = head;
}
return node == null ? null : node.val;
}
private static class ListNode<K, V> {
private final K key;
private V val;
private ListNode<K, V> next;
private ListNode<K, V> prev;
public ListNode(K key, V val) {
this.key = key;
this.val = val;
}
@Override
public String toString() {
return "ListNode{" +
"key=" + key +
", val=" + val +
'}';
}
}
@Override
public String toString() {
return "LRUCache{" +
"map=" + map +
'}';
}
}
复杂度
时间复杂度:O(1)
空间复杂度:O(capacity)
硬广告
欢迎关注公众号:double6
final
本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情