A cache keeps a few recent results close at hand so they are quick to read again. It can only hold a limited number of entries, so when it is full it must throw one away. An LRU (Least Recently Used) cache throws away the entry that was used longest ago. Build a class LRUCache that stores integer keys with integer values.
The class keeps: private val capacity: Int (declared in the class header), a MutableMap<Int, Int> called map (key to value) and a MutableList<Int> called order (the keys, least recently used first).
Create it: LRUCache(capacity) makes an empty cache that holds at most capacity entries.
Methods and rules:
fun get(key: Int): Intreturns the value stored forkey. A hit counts as a use: the key becomes the most recently used. If the key is not in the cache (never stored, or already evicted), return-1and change nothing.fun put(key: Int, value: Int)stores the value forkey, and the key becomes the most recently used.- If the key is already in the cache, replace its value. Nothing is evicted.
- If the key is new and the cache already holds
capacityentries, first evict the least recently used entry (remove it from the map and fromorder), then store the new one.
- Keep
ordersorted by use: the least recently used key at the front, the most recently used at the end. Every use moves the key to the end.
Tools for this language:
- The primary constructor is the class header:
class LRUCache(private val capacity: Int). The fields aremutableMapOf()andmutableListOf(). map.containsKey(key)tells whether a key is stored;map[key] = valuestores or replaces a value;map.remove(key)deletes an entry;map.sizecounts the entries.order.remove(key)removes an item by value (the list holdsInt),order.add(key)adds at the end, andorder[0]withorder.removeAt(0)reads and removes the first item.
This version walks over the list of keys for each operation, which is fine for learning. Interview solutions reach O(1) with a hash map plus a doubly linked list.
You start with the code that tests your class (write LRUCache above it):
class Solution {
fun mainFunction() {
val c = LRUCache(2)
c.put(1, 10)
c.put(2, 20)
println("Get 1: " + c.get(1))
c.put(3, 30)
println("Get 2: " + c.get(2))
println("Get 3: " + c.get(3))
println("Miss 9: " + c.get(9))
c.put(1, 11)
println("Update 1: " + c.get(1))
c.put(4, 40)
println("Evicted 3: " + c.get(3))
println("Kept 1: " + c.get(1))
println("Kept 4: " + c.get(4))
val s = LRUCache(1)
s.put(1, 100)
s.put(2, 200)
println("Small 1: " + s.get(1))
println("Small 2: " + s.get(2))
}
}
Expected output:
Get 1: 10
Get 2: -1
Get 3: 30
Miss 9: -1
Update 1: 11
Evicted 3: -1
Kept 1: 11
Kept 4: 40
Small 1: -1
Small 2: 200
Check it against the rules: Get 2 is -1 because key 2 was the least recently used when key 3 arrived (key 1 had just been read). Updating key 1 to 11 does not evict anything. With capacity 1, the second put evicts the first key.
Two structures work together: the map gives a fast lookup of the value, and
order remembers which key was used when. Both must always hold the same keys.
order.remove(key) then order.add(key) moves a key to the end. order[0] is the least recently used key.
class LRUCache(private val capacity: Int) {
private val map: MutableMap<Int, Int> = mutableMapOf()
private val order: MutableList<Int> = mutableListOf()
fun get(key: Int): Int {
if (!map.containsKey(key)) return -1
order.remove(key)
order.add(key)
return map[key]!!
}
fun put(key: Int, value: Int) {
if (map.containsKey(key)) {
order.remove(key)
} else if (map.size >= capacity) {
val evict = order[0]
order.removeAt(0)
map.remove(evict)
}
map[key] = value
order.add(key)
}
}
Free plan: 30 tutor messages. Premium is unlimited.
