Reference: LeetCode
Difficulty: Medium
Problem
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations:
get
andput
.
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.
The cache is initialized with a positive capacity.
Example:
1 | LRUCache cache = new LRUCache(2); |
Follow up: Could you do both operations in O(1)
time complexity?
Analysis
Use LinkedHashMap
Reference: Java LinkedHashMap工作原理及实现
From official documentation:
Hash table
andlinked list
implementation of the Map interface, with predictable iteration order. This implementation differs from HashMap in that it maintains adoubly-linked list
running through all of its entries. This linked list defines the iteration ordering, which is normally the order in which keys were inserted into the map (insertion-order).
Inside:
In HashMap
, there are three callback methods that allow LinkedHashMap post-actions.
1 | void afterNodeAccess(Node<K,V> p) { } |
removeEldestEntry
is a method that we can override. It is usually invoked after put
operation. If it returns true
, the first entry would be deleted; otherwise, nothing happens.
Then we can see how put
and get
work. In LinkedHashMap, put
is not re-implemented, but all new features are implemented in afterNodeAccess
and afterNodeInsertion
callbacks.
1 | // `get` is re-implemented |
accessOrder:
accessOrder
is a boolean that indicates two modes of structural modification
, which are access-ordered
and insertion-ordered
modes.
A structural modification is any operation that adds or deletes one or more mappings or, in the case of
access-ordered
linked hash maps, affects iteration order. Ininsertion-ordered
linked hash maps, merely changing the value associated with a key that is already contained in the map is not a structural modification. Inaccess-ordered
linked hash maps, merely querying the map with get is a structural modification.
In other words, when accessOrder
is set as true
, calls of put
, get
, and so on would trigger affection on iteration order. The accessed entry will be put at the end of the linked list.
Basic Usage:
accessOrder
is false
:
1 | // Default mode is insertion-order mode |
accessOrder
is true
:
1 | // access-ordered mode |
Code for this problem:
We can make the class LRUCache
extend from LinkedHashMap
. get
and put
are overloaded, and removeEldestEntry
is overridden. It means that we would delete the first entry when this.size() > capacity
.
Note: capacity
member is a fixed value, while the capacity passed into LinkedHashMap
is for speculative purpose (just guessing? optimization?).
1 | class LRUCache extends LinkedHashMap<Integer, Integer> { |
The second approach is similar to the object adapter
pattern. A LinkedListMap
object is wrapped inside LRUCache
.
Note: See comments below.
1 | class LRUCache { |
Time: $O(1)$
Space: $O(capacity)$
Do It By Yourself
欣赏一下别人的代码。我稍微改了一下。自己写了好久还是有 bug,链表有毒。
1 | public class LRUCache { |
Time: $O(1)$
Space: $O(capacity)$