- 构造一个LRU Cache的数据结构,它应该有一下两个方法:get和set
- get(key): 如果Cache中Key存在,则返回它的值(假设为正数),否则返回-1.
- set(key, value) :设置新值;如果Cache中key存在,则插入新的value。如果Cache达到它到capacity,在插入一个新项之前应该删除最久不使用的项。
- 举例:
// Let’s say we have a LRU cache of capacity 2.
LRUCache cache = new LRUCache(2);
cache.set(1, 10); // it will store a key (1) with value 10 in the cache.
cache.set(2, 20); // it will store a key (2) with value 20 in the cache.
cache.get(1); // returns 10
cache.set(3, 30); // evicts key 2 and store a key (3) with value 30 in the cache.
cache.get(2); // returns -1 (not found)
cache.set(4, 40); // evicts key 1 and store a key (4) with value 40 in the cache.
cache.get(1); // returns -1 (not found)
cache.get(3); // returns 30
cache.get(4); // returns 40
- 解答
- 暴力方法
假定我们有一个存储Node的数组,每个Node包含以下信息
class Node {
int key;
int value;
// it shows the time at which the key is stored.
// We will use the timeStamp to find out the
// least recently used (LRU) node.
int timeStamp;
public Node(int key, int value)
{
this.key = key;
this.value = value;
// currentTimeStamp from system
this.timeStamp = currentTimeStamp;
}
}
数组的大小就是Cache到容量capacity。
- get(int key): 遍历一遍,如果存在则返回对应到value,否则返回-1。 时间复杂度:O(n)
- set(int key, int value):如果数组已经满了,我们需要从数组中删除一个元素,为了找到满足LRU到节点Node,我们遍历一遍,通过时间戳timeStamp找到最旧的那个值。然后就可以插入新增到节点Node。如果数组未满,我们直接插入新增到节点Node。 时间复杂度:O(n)
- 最优方法
解决这个问题到关键是使用双端队列,因为它可以快速的移动节点Nodes。
LRU Cache是一个键值对为Key-Node的HashMap,HashMap可以使get()的时间复杂度是O(1)。双端队列可以是Node的操作:adding/removal到时间复杂度是O(1)。
import java.util.HashMap;
class Node {
int key;
int value;
Node pre;
Node next;
public Node(int key, int value)
{
this.key = key;
this.value = value;
}
}
class LRUCache {
private HashMap<Integer, Node> map;
private int capacity, count;
private Node head, tail;
public LRUCache(int capacity)
{
this.capacity = capacity;
map = new HashMap<>();
head = new Node(0, 0);
tail = new Node(0, 0);
head.next = tail;
tail.pre = head;
head.pre = null;
tail.next = null;
count = 0;
}
public void deleteNode(Node node)
{
node.pre.next = node.next;
node.next.pre = node.pre;
}
public void addToHead(Node node)
{
node.next = head.next;
node.next.pre = node;
node.pre = head;
head.next = node;
}
// This method works in O(1)
public int get(int key)
{
if (map.get(key) != null) {
Node node = map.get(key);
int result = node.value;
deleteNode(node);
addToHead(node);
System.out.println("Got the value : " + result
+ " for the key: " + key);
return result;
}
System.out.println("Did not get any value"
+ " for the key: " + key);
return -1;
}
// This method works in O(1)
public void set(int key, int value)
{
System.out.println("Going to set the (key, "
+ "value) : (" + key + ", "
+ value + ")");
if (map.get(key) != null) {
Node node = map.get(key);
node.value = value;
deleteNode(node);
addToHead(node);
}
else {
Node node = new Node(key, value);
map.put(key, node);
if (count < capacity) {
count++;
addToHead(node);
}
else {
map.remove(tail.pre.key);
deleteNode(tail.pre);
addToHead(node);
}
}
}
}
public class TestLRUCache {
public static void main(String[] args)
{
System.out.println("Going to test the LRU "
+ " Cache Implementation");
LRUCache cache = new LRUCache(2);
// it will store a key (1) with value
// 10 in the cache.
cache.set(1, 10);
// it will store a key (1) with value 10 in the
// cache.
cache.set(2, 20);
System.out.println("Value for the key: 1 is "
+ cache.get(1)); // returns 10
// evicts key 2 and store a key (3) with
// value 30 in the cache.
cache.set(3, 30);
System.out.println(
"Value for the key: 2 is "
+ cache.get(2)); // returns -1 (not found)
// evicts key 1 and store a key (4) with
// value 40 in the cache.
cache.set(4, 40);
System.out.println(
"Value for the key: 1 is "
+ cache.get(1)); // returns -1 (not found)
System.out.println("Value for the key: 3 is "
+ cache.get(3)); // returns 30
System.out.println("Value for the key: 4 is "
+ cache.get(4)); // return 40
}
}
输出:
Going to test the LRU Cache Implementation
Going to set the (key, value) : (1, 10)
Going to set the (key, value) : (2, 20)
Got the value : 10 for the key: 1
Value for the key: 1 is 10
Going to set the (key, value) : (3, 30)
Did not get any value for the key: 2
Value for the key: 2 is -1
Going to set the (key, value) : (4, 40)
Did not get any value for the key: 1
Value for the key: 1 is -1
Got the value : 30 for the key: 3
Value for the key: 3 is 30
Got the value : 40 for the key: 4
Value for the key: 4 is 40
另外一种实现方式: 使用LinkedHashMap。
removeEldestEntry()被重写,目的是当大小超过容量时,需要移除旧的Mapping。
import java.util.LinkedHashMap;
import java.util.Map;
class LRUCache {
private LinkedHashMap<Integer, Integer> map;
private final int CAPACITY;
public LRUCache(int capacity)
{
CAPACITY = capacity;
map = new LinkedHashMap<Integer, Integer>(capacity, 0.75f, true) {
protected boolean removeEldestEntry(Map.Entry eldest)
{
return size() > CAPACITY;
}
};
}
// This method works in O(1)
public int get(int key)
{
System.out.println("Going to get the value " +
"for the key : " + key);
return map.getOrDefault(key, -1);
}
// This method works in O(1)
public void set(int key, int value)
{
System.out.println("Going to set the (key, " +
"value) : (" + key + ", " + value + ")");
map.put(key, value);
}
}
public class TestLRUCacheWithLinkedHashMap {
public static void main(String[] args)
{
System.out.println("Going to test the LRU "+
" Cache Implementation");
LRUCache cache = new LRUCache(2);
// it will store a key (1) with value
// 10 in the cache.
cache.set(1, 10);
// it will store a key (1) with value 10 in the cache.
cache.set(2, 20);
System.out.println("Value for the key: 1 is " +
cache.get(1)); // returns 10
// evicts key 2 and store a key (3) with
// value 30 in the cache.
cache.set(3, 30);
System.out.println("Value for the key: 2 is " +
cache.get(2)); // returns -1 (not found)
// evicts key 1 and store a key (4) with
// value 40 in the cache.
cache.set(4, 40);
System.out.println("Value for the key: 1 is " +
cache.get(1)); // returns -1 (not found)
System.out.println("Value for the key: 3 is " +
cache.get(3)); // returns 30
System.out.println("Value for the key: 4 is " +
cache.get(4)); // return 40
}
}
输出:
Going to test the LRU Cache Implementation
Going to set the (key, value) : (1, 10)
Going to set the (key, value) : (2, 20)
Going to get the value for the key : 1
Value for the key: 1 is 10
Going to set the (key, value) : (3, 30)
Going to get the value for the key : 2
Value for the key: 2 is -1
Going to set the (key, value) : (4, 40)
Going to get the value for the key : 1
Value for the key: 1 is -1
Going to get the value for the key : 3
Value for the key: 3 is 30
Going to get the value for the key : 4
Value for the key: 4 is 40