The initial mechanics are pretty okay. We need to:
- Initialize the LRUCache with a capacity (which needs to be always positive)
- Have a way to retrieve the value of a key – this is a hint for using a hashmap, so we want to use a map as the backend data structure here. Return -1 if not found.
- putting a new key is interesting. We need to: 3.1. Update the key if it exists 3.2. Add the key/value pair to the cache.
However, in order to add the key/value to the cache, we need to:
- Everytime we add the key to the cache, we need to increment a counter.
- If counter + 1 > capacity, we need to evict.
- We need a function behind the scenes called evict. It will delete() the least recently used key.
- We have a challenge – representing data as a hashmap is the obvious choice for retrieving data. However, understanding which are the least used elements might require a priority queue. We can map keys with lowest priorities to be the ones to be evicted.
I believe we would like to use min-heap as the data structure for our priority queue.
It's also important to recall that one can represent binary trees in arrays by using:
2i + 1 for the left child 2i + 2 for the right child
An array-represented binary tree looks like this:
I need to find a way to properly calculate leaves. I believe the proper way to do it is to simply check if the index for the left child from the current position is smaller than the size of the current heap.
It seems that finding the parent requires you to use basic algebra. The same formula for finding the parent from a left standpoint works for a right standpoint.
For example: 2 is the parent of 5 and 6.
2x + 1 = 5 x = (5 - 1) / 2 x = 2.5 (which gets casted as integer and becomes 2)
2x + 2 = 6 x = (6 - 2) / 2 x = 2
In the end, it was a matter of understanding that recently added items are also recently accessed items, so we keep their priority low.
- I think I misunderstood the LRU idea – it's not least used, it's least recently used.
Instead of counting how many times it was accessed, we can keep a counter of "when" it was accessed.
But I also don't have to confuse with "last recently used". Which means that the combination between access count and time matters.
After I finished my submission, I checked Leetcode's solution. Although it's more performatic, I find this one neat and quite understandable. Their solution with doubly liked lists is probably the best, but in this one I used a heap to help me.
So, I created the structure for a min-heap where the key that determines whether the object goes up or down is a timestamp. I also move things around the heap when I update the timestamp, to represent the least recently used object on the top.
In the end the solution was quite clear, albeit overengineered. I loved working on it :)