Suppose we want to implement a data structure for Least Frequently Used (LFU) cache system. It should support the following operations:
get(key) − This helps to get the value of the key if the key exists in the cache, otherwise return −1.
set(key, value) − This will be used to set or insert the value if the key is not already present.
When the cache reached its maximum capacity, it should invalidate the least frequently used element before inserting a new element.
So, if the LFUCache is initialized with capacity 2 and call these methods cache.set(1, 1); cache.set(2, 2); cache.get(1); cache.set(3, 3); cache.get(2); cache.set(4, 4); cache.get(1); cache.get(3); cache.get(4); then the output will be 1,−1,1,−1,4 respectively.
To solve this, we will follow these steps −
The initializer will take the capacity value
remain := capacity
least_freq := 1
node_for_freq := a map to hold the data according to insert orders
node_for_key := a new map
Define a function _update() . This will take key, value
x, freq := node_for_key[key]
delete element from node_for_freq[freq] corresponding to key
if size of node_for_freq[least_freq] is same as 0, then
least_freq := least_freq + 1
node_for_freq[freq+1, key] := value, freq+1
node_for_key[key] := value, freq+1
Define a function get() . This will take key
if key not in node_for_key is non−zero, then
return −1
value := node_for_key[key, 0]
_update(key, value)
return value
Define a function set() . This will take key, value
if key in node_for_key is non−zero, then
_update(key, value)
otherwise,
node_for_key[key] := value,1
node_for_freq[1, key] := value,1
if remain is same as 0, then
removed := delete one element from node_for_freq[least_freq] in fifo order
delete element from node_for_key corresponds to key removed[0]
otherwise,
remain := remain − 1
least_freq := 1
Let us see the following implementation to get better understanding −
Example
from collections import defaultdict, OrderedDict class LFUCache: def __init__(self, capacity): self.remain = capacity self.least_freq = 1 self.node_for_freq = defaultdict(OrderedDict) self.node_for_key = dict() def _update(self, key, value): _, freq = self.node_for_key[key] self.node_for_freq[freq].pop(key) if len(self.node_for_freq[self.least_freq]) == 0: self.least_freq += 1 self.node_for_freq[freq+1][key] = (value, freq+1) self.node_for_key[key] = (value, freq+1) def get(self, key): if key not in self.node_for_key: return −1 value = self.node_for_key[key][0] self._update(key, value) return value def set(self, key, value): if key in self.node_for_key: self._update(key, value) else: self.node_for_key[key] = (value,1) self.node_for_freq[1][key] = (value,1) if self.remain == 0: removed = self.node_for_freq[self.least_freq].popitem(last=False) self.node_for_key.pop(removed[0]) else: self.remain −= 1 self.least_freq = 1 cache = LFUCache(2) cache.set(1, 1) cache.set(2, 2) print(cache.get(1)) cache.set(3, 3) print(cache.get(2)) cache.set(4, 4) print(cache.get(1)) print(cache.get(3)) print(cache.get(4))
Input
cache.set(1, 1) cache.set(2, 2) cache.get(1) cache.set(3, 3) cache.get(2) cache.set(4, 4) cache.get(1) cache.get(3) cache.get(4)
Output
1 −1 1 −1 4