【算法】【线性表】【链表】LRU 缓存
1 题目
请你设计并实现一个满足 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
提示:
1 <= capacity <= 3000
0 <= key <= 10000
0 <= value <= 105
- 最多调用
2 * 105
次get
和put
2 解答
今天脑袋不在线,这前后指针把我指晕了,= =但我还是硬着头皮自己写出来了:
大致思路:首先操作要保证O(1) 那么需要一个map来存放数据,并且要移动或者操作首尾元素,所以我要用一个双向链表哈:
public class LRUCache { class DoubleNode { int key; int val; DoubleNode pre; DoubleNode next; DoubleNode(int key, int val) { this.key = key; this.val = val; } DoubleNode(int key, int val, DoubleNode pre, DoubleNode next) { this.key = key; this.val = val; this.pre = pre; this.next = next; } } // 容量 private int capacity; // 存储数据 private Map<Integer, DoubleNode> dataMap; // 头节点 因为操作都要保证 O(1) 所以用双向链表 private DoubleNode head; public LRUCache(int capacity) { this.capacity = capacity; head = new DoubleNode(0, 0); dataMap = new HashMap<>(capacity); } public int get(int key) { DoubleNode node = dataMap.get(key); // 如果为空表示不存在,直接返回 -1 if (Objects.isNull(node)) { return -1; } // 存在的话 需要把该 key 移动到最前面来 moveFirst(node); return node.val; } public void put(int key, int value) { // 容量本身是空的话 直接返回 if (capacity <= 0) return; // 先看他存在不 DoubleNode node = dataMap.get(key); // 存在该key,那么更新下旧值,并移动到前面来 if (Objects.nonNull(node)) { node.val = value; moveFirst(node); return; } node = new DoubleNode(key, value); // 不存在的话,当前容量不够了先删除 if (dataMap.size() >= capacity) { // 容量满了的话 先淘汰再放 removeLast(); } addFirst(node); dataMap.put(key, node); } /** * 删除尾部元素 */ public void removeLast() { DoubleNode tailNode = head.pre; if (tailNode == null) { return; } DoubleNode tailPreNode = tailNode.pre; if (tailPreNode == head) { head.pre = null; head.next = null; } else { tailPreNode.next = tailNode.next; head.pre = tailPreNode; } dataMap.remove(tailNode.key); } /** * 新增元素到头部 * @param node */ public void addFirst(DoubleNode node) { DoubleNode nextNode = head.next; if (nextNode == null) { node.pre = head; node.next = head; head.next = node; head.pre = node; } else { node.pre = head; node.next = nextNode; head.next = node; nextNode.pre = node; } } /** * 移动元素到最前面来 * @param node */ public void moveFirst(DoubleNode node) { DoubleNode preNode = node.pre; // 如果 preNode == head 已经在最前面了 就不需要移动了 if (preNode == head) { return; } // node 前边的 next 更改 preNode.next = node.next; DoubleNode nextNode = node.next; // node 后边的 pre 更改 nextNode.pre = preNode; // node 解放出来了 插入到 head 的下边 addFirst(node); } }
加油。