LeetCode 146. LRU缓存
请你设计并实现一个满足 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 <= capacity <= 3000
0 <= key <= 10000
0 <= value <= 105
- 最多调用
2 * 105
次get
和put
思路
基本的想法是,用一个HashMap来存储键值对,再用一个LinkedList来存储访问序列。这样是可以做到get
操作查询是put
和get
都要维护访问链,就导致时间复杂度增加至
好消息是,Java为我们提供了一个非常有用的类LinkedHashMap
。虽然LinkedHashMap增加了时间和空间上的开销,但是它通过维护一个额外的双向链表保证了迭代顺序。特别地,该迭代顺序可以是插入顺序,也可以是访问顺序。因此,根据链表中元素的顺序可以将LinkedHashMap
分为:保持插入顺序的LinkedHashMap
和保持访问顺序的LinkedHashMap
,其中LinkedHashMap
的默认实现是按插入顺序排序的。
LinkedHashMap增加了两个属性用于保证迭代顺序,分别是 双向链表头结点header 和 标志位accessOrder (值为true
时,表示按照访问顺序迭代;值为false
时,表示按照插入顺序迭代)。
/** |
LinkedHashMap
一共提供了五个构造函数,它们都是在HashMap
的构造函数的基础上实现的,分别如下:
/** |
该构造函数意在构造一个指定初始容量和默认负载因子 (0.75)的空LinkedHashMap
/** |
该构造函数意在构造一个指定初始容量和指定负载因子的空LinkedHashMap
/** |
该构造函数意在构造一个与指定Map
具有相同映射的LinkedHashMap
,其 初始容量不小于16(具体依赖于指定Map
的大小),负载因子是 0.75,是 Java Collection Framework 规范推荐提供的,
/** |
该构造函数意在构造一个指定初始容量和指定负载因子的具有指定迭代顺序的LinkedHashMap
/** |
注意到我们在前面介绍的LinkedHashMap
的五种构造方法,前四个构造方法都将accessOrder
设为false
,说明默认是按照插入顺序排序的;而第五个构造方法可以自定义传入的accessOrder
的值,因此可以指定双向循环链表中元素的排序规则。特别地,当我们要用LinkedHashMap
实现LRU算法时,就需要调用该构造方法并将accessOrder
置为true
。
所以我们可以直接使用LinkedHashMap
,并指定它维护访问顺序,实现LRU算法。在本题中,我们还需要重写LinkedHashMap
的removeEldestEntry
方法。该方法的描述如下。
/** |
简单来说,就是若该方法返回true
则会将访问链尾部的元素剔除。该方法的调用时机是每次put
方法插入新元素之后。
class LRUCache { |
复杂度
- 时间复杂度:
。 - 空间复杂度:
。