LRU Cache
Problem
Simulate a Least Recently Used (LRU) cache. The first line is the capacity. Each next line is put K V or get K. put stores the value (and replaces an old value). If the cache has more than capacity keys after a put, the least recently used key is removed. A put or a successful get makes the key the most recently used. For every get print the value, or -1 if the key is not in the cache.
Constraints
- 1 <= capacity <= 1000
- 1 <= commands <= 10^4
Examples
Input
2 put 1 1 put 2 2 get 1 put 3 3 get 2
Output
1 -1
Solutions
An LRU cache throws away the key that was used longest ago. A map that remembers the order of use does this in O(1): move a key to the end when it is used, and remove from the front when the cache is full.
/*
* Problem : Process commands line by line.
* Input : Several lines of commands.
* Output : The answers of the get commands.
* Approach: Keep keys in order of use.
* Time : O(1) per operation
* Space : O(capacity)
* Java : 8 or newer.
*/
import java.util.Scanner;
public class Main {
// Step 1: LinkedHashMap with accessOrder = true keeps keys from least recently used to most recently used.
// removeEldestEntry is called after each put. We remove the oldest entry when the size is above the capacity.
static class LruCache extends java.util.LinkedHashMap<Integer, Integer> {
private final int capacity;
LruCache(int capacity) {
super(16, 0.75f, true);
this.capacity = capacity;
}
@Override
protected boolean removeEldestEntry(java.util.Map.Entry<Integer, Integer> eldest) {
return size() > capacity;
}
}
// This method solves the task. It receives the input and returns the answer.
static String[] solve(String[] lines) {
LruCache cache = new LruCache(Integer.parseInt(lines[0]));
java.util.List<String> output = new java.util.ArrayList<>();
for (int i = 1; i < lines.length; i++) {
String[] parts = lines[i].split(" ");
// Step 2: put stores the value. The map removes the oldest entry by itself when it is too big.
if (parts[0].equals("put")) {
cache.put(Integer.parseInt(parts[1]), Integer.parseInt(parts[2]));
} else {
// Step 3: get returns -1 for a missing key. A successful get also marks the key as recently used.
Integer value = cache.get(Integer.parseInt(parts[1]));
output.add(value == null ? "-1" : String.valueOf(value));
}
}
return output.toArray(new String[0]);
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
java.util.List<String> all = new java.util.ArrayList<>();
while (scanner.hasNextLine()) {
String line = scanner.nextLine().trim();
if (!line.isEmpty()) {
all.add(line);
}
}
String[] lines = all.toArray(new String[0]);
System.out.println(String.join("\n", solve(lines)));
}
}
Hints (2)
- Which key is removed when the cache is full?
- What happens to a key after a get?