题目描述
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。
练习地址
实现
- 哈希+双向链表
- 复杂度分析
时间复杂度:对于 put 和 get 都是 O(1)。
空间复杂度:O(capacity),因为哈希表和双向链表最多存储 capacity+1 个元素。
1、解法 O(1)
- 基本思路
- 出现key、value - 想到哈希表 O(1)
- 出入顺序 - 栈、队列、链表
- 双向链表:linkedHashMap
public 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;
}
}
- 非封装方法实现(实现简单的双向链表)
capacity =2;
cache.put(1,1);
cache.put(2,2);
cache.get(1);
cache.put(3,3);
- 代码实现
注:上述流程图是,最近使用放在尾部,超出容量删除头部
代码是,最近使用放在头部,超出容量删除尾部
public class LRUCache {
class ListNode {
int key;
int value;
ListNode pre;
ListNode next;
public ListNode() {};
public ListNode(int key, int value) {
this.key = key;
this.value = value;
}
}
private Map<Integer, ListNode> cache = new HashMap<>();
private int size;
private int capacity;
//双向链表的实现中,使用一个伪头部(dummy head)和伪尾部(dummy tail)标记界限,
// 这样在添加节点和删除节点的时候就不需要检查相邻的节点是否存在。 (比如删除与尾节点连接的节点)
private ListNode head, tail;
public LRUCache(int capacity) {
this.size = 0;
this.capacity = capacity;
head = new ListNode();
tail = new ListNode();
head.next = tail;
tail.pre = head;
}
public int get(int key) {
ListNode node = cache.get(key);
// 不存在,返回-1
if (node == null) return -1;
// 存在,先用哈希表定位,在移至头部
moveTohead(node);
return node.value;
}
public void put(int key, int value) {
ListNode node = cache.get(key);
// 不存在,创建一个节点
if (node == null) {
ListNode newNode = new ListNode(key, value);
cache.put(key, newNode);
addTohead(newNode);
++size;
//超过容量,链表、哈希删除尾
if (size > capacity) {
ListNode tail = removeTail();
cache.remove(tail.key);
--size;
}
} else { //key存在,hash找到位置,修改value,置头
node.value = value;
moveTohead(node);
}
}
// 移至头部
private void moveTohead(ListNode node) {
removeNode(node);
addTohead(node);
}
private void removeNode(ListNode node) {
node.pre.next = node.next;
node.next.pre = node.pre;
}
private void addTohead(ListNode node) {
node.pre = head;
node.next = head.next;
head.next.pre = node;
head.next = node;
}
private ListNode removeTail() {
ListNode node = tail.pre;
removeNode(node);
return node;
}
}