题目描述
请你设计并实现一个满足 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)
的平均时间复杂度运行。
示例 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 <= 10^5
- 最多调用
2 * 10^5
次get
和put
哈希表 + 双向链表
核心思路
使用 哈希表 实现 O(1)
时间复杂度的键值查找,通过 双向链表 维护访问顺序:
- 链表头部存放最近访问的节点
- 链表尾部存放最久未使用的节点
- 伪头尾节点简化链表边界操作
关键操作图解
- 添加节点到头部
1
2
3
4
5head <-> [node] <-> oldFirst
1. node.prev = head
2. node.next = head.next
3. head.next.prev = node
4. head.next = node - 移除节点
1
2
3
4prevNode <-> [node] <-> nextNode
=> prevNode <-> nextNode
1. node.prev.next = node.next
2. node.next.prev = node.prev
代码实现
1 | public class LRUCache { |
复杂度分析
- 时间复杂度:
O(1)
,哈希表查找O(1)
,链表操作O(1)
。 - 空间复杂度:
O(n)
,哈希表存储n
个键值对,双向链表存储n+2
个节点(含伪头尾)。
应用场景
- 数据库查询缓存
- 浏览器页面缓存
- 操作系统页面置换算法
- 微服务API响应缓存
关键点总结:哈希表保证快速访问,双向链表维护访问顺序,伪头尾节点简化边界处理。通过O(1)时间完成核心操作,是工业级LRU缓存的经典实现。