LFU Cache in Python


Suppose we want to design and implement a data structure for Least Frequently Used (LFU) cache system. It should support the following operations −

  • get(key) – This will be used to get the value of the key if the key exists in the cache, otherwise return -1.

  • put(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.put(1, 1); cache.put(2, 2); cache.get(1); cache.put(3, 3); cache.get(2); cache.put(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 put() . 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

Example 

Let us see the following implementation to get better understanding −

 Live Demo

import collections
class LFUCache:
   def __init__(self, capacity):
      self.remain = capacity
      self.least_freq = 1
      self.node_for_freq = collections.defaultdict(collections.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 put(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.put(1, 1)
cache.put(2, 2)
print(cache.get(1))
cache.put(3, 3)
print(cache.get(2))
cache.put(4, 4)
print(cache.get(1))
print(cache.get(3))
print(cache.get(4))

Input

cache.put(1, 1)
cache.put(2, 2)
cache.get(1)
cache.put(3, 3)
cache.get(2)
cache.put(4, 4)
cache.get(1)
cache.get(3)
cache.get(4)

Output

1
-1
1
-1
4

Updated on: 21-Jul-2020

2K+ Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements