题目:
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and put.
get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
put(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.
Follow up:
Could you do both operations in O(1) time complexity?
Example:
LRUCache cache = new LRUCache( 2 /* capacity */ );
cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // returns 1
cache.put(3, 3); // evicts key 2
cache.get(2); // returns -1 (not found)
cache.put(4, 4); // evicts key 1
cache.get(1); // returns -1 (not found)
cache.get(3); // returns 3
cache.get(4); // returns 4
题目大意
这是一个经典的题目,设计一个LRUCache
。LRU 是一个缓存算法, 在实际编程中使用的很广泛,比如页面置换算法和图片缓存。
思路
思路其实就是理解LRU
这个算法,然后按照思路去实现就好。
Java
其实已经实现了这个算法,重写LinkedHashMap
的removeEldestEntry
方法,给定删除元素的触发条件即可。至于LinkedHashMap
内部是如何实现的这里不再赘述,有兴趣的可以去看看源码。这里我使用了另外一种实现方式,HashMap
加双向链表。
对外的API
主要有两个,put
和get
,其实应该还有一个clear
方法,用于清空整个缓存,这个方法实现较为简单,只需要把双向链表首尾置空,然后初始化HashMap
即可。
其实LRUCache
里还有两个很重要的内部方法moveToFirst
和trimToSize
,moveToFirst
主要完成将元素放到队首的操作,trimToSize
则将缓存数量恢复到额定大小。
get
按照LRU
的原理,每次执行get
方法先检查缓存里是否存在,不存在则直接返回空,存在则将该元素放到双向链表的队首并返回。
如果没有HashMap
的话,检查缓存是否存在是一个比较消耗时间的操作,需要从队首开始遍历一直到队尾,用HashMap
能加速访问。
put
put
方法比较复杂,要做的操作很多。
首先检查缓存里是否存在,如果存在则更新value
值将其放到队首;如果不存在则新建一个key-value
对象,将这个新建的对象放到HashMap
和双向链表的队首,之后还需要检查当前数据数量是否超过缓存大小,如果超过了则需要去除队尾的数据。
代码实现
这里按照Leetcode
上的要求完成,key
和value
都是Integer
类型,但实际使用编写的时候最好使用泛型。
1 | class LRUCache { |
Leetcode
运行时间不稳定,一样的代码两次提交运行时间一样,这里放一张时间更短的。