Design and implement a data structure for Least frequently Used (LFU) cache. It should support the following operations: get
and put
.
get(key)
-Get The value ('ll always be positive) of the key if the key exists in the cache, otherwise return-1.
put(key, value)
-Set or insert the value if the key is not already present. When the cache is reaches its capacity, it should invalidate the least frequently used item before inserting a new item. For the purpose of this problem, when there is a tie (i.e., b or more keys, which has the same frequency), the least rece Ntly used key would be evicted.
Follow up:
Could do both operations in O (1) time complexity?
Example:
Lfucache cache = new Lfucache (2/* capacity *); Cache.put (1, 1); Cache.put (2, 2); Cache.get (1); Returns 1cache.put (3, 3); Evicts key 2cache.get (2); Returns-1 (not found) Cache.get (3); Returns 3.cache.put (4, 4); Evicts key 1.cache.get (1); Returns-1 (not found) Cache.get (3); Returns 3cache.get (4); Returns 4
Doubly linked list (doubly Linked list) + hash table (hash table)
Java:
public class Lfucache {Node head = null; final int capacity; Map<integer, integer> ValueMap; Map<integer, node> Nodemap; public Lfucache (int capacity) {this.capacity = capacity; ValueMap = new Hashmap<> (this.capacity, 1f); Nodemap = new Hashmap<> (this.capacity, 1f); } public int get (int key) {if (Valuemap.containskey (key)) Increase (key, Valuemap.get (key)); Return Valuemap.getordefault (Key,-1); } private void increase (int key, int value) {node node = nodemap.get (key); Node.keys.remove (key); if (Objects.isnull (node.next)) Node.next = new node (node, NULL, 1 + node.count, key); else if (Node.next.count = = Node.count + 1) node.next.keys.add (key); else Node.next = Node.next.prev = new node (node, Node.next, Node.count + 1, key); Nodemap.put (key, Node.next); Valuemap.put (key, value); if (Node.keys.isEmpty ()) Remove (node); } private void RemovE (node node) {if (head = = Node) head = Node.next; else Node.prev.next = Node.next; if (Objects.nonnull (node.next)) Node.next.prev = Node.prev; } public void set (int key, int value) {if (0 = = this.capacity) return; if (Valuemap.containskey (key)) {Increase (key, value); } else {if (valuemap.size () = = this.capacity) remove (); Valuemap.put (key, value); Add (key); }} private void Add (int key) {if (Objects.isnull (head)) head = new Node (null, NULL, 1, key); else if (Head.count = = 1) head.keys.add (key); else head = Head.prev = new Node (null, head, 1, key); Nodemap.put (key, head); } private void Remove () {if (Objects.isnull (head)) return; int oldest = Head.keys.iterator (). Next (); Head.keys.remove (oldest); if (Head.keys.isEmpty ()) remove (head); Nodemap.remove (oldest); Valuemap.remove (oldest); } class Node { Public Node prev, next; public final int count; Public linkedhashset<integer> keys = new linkedhashset<> (); Public node (node prev, node next, int count, int key) {This.prev = prev; This.next = Next; This.count = count; Keys.add (key); } }}
Python:
Class Keynode (object): Def __init__ (self, key, value, Freq = 1): Self.key = key Self.value = value Self.freq = Freq Self.prev = Self.next = Noneclass Freqnode (object): Def __init__ (self, freq, prev, next): Self.freq = freq Self.prev = prev Self.next = Next Self.first = Self.last = Noneclass Lfucache (obj ECT): def __init__ (self, capacity): "" ": Type Capacity:int" "" "self.capacity = Cap Acity self.keydict = dict () self.freqdict = Dict () Self.head = None def get (self, Key): "" " : Type Key:int:rtype:int "" "if key in Self.keyDict:keyNode = Self.keydict[key] Value = Keynode.value self.increase (key, value) return value Return-1 def set (Self, key, value): ' "": Type Key:int:type value:int:rtype:void "" "if self . Capacity = = 0: return if key in Self.keyDict:self.increase (key, value) return if Len (self.keydict ) = = Self.capacity:self.removeKeyNode (self.head.last) Self.insertkeynode (key, value) def increase (SE LF, key, value): "" "increments the freq of an existing Keynode<key, value> by 1. : Type Key:str:rtype:void "" "Keynode = Self.keydict[key] Keynode.value = value freqn Ode = self.freqdict[keynode.freq] Nextfreqnode = freqnode.next Keynode.freq + = 1 if Nextfreqnode is N One or Nextfreqnode.freq > KeyNode.freq:nextFreqNode = Self.insertfreqnodeafter (Keynode.freq, Freqnode) Self.unlinkkey (Keynode, Freqnode) Self.linkkey (Keynode, Nextfreqnode) def insertkeynode (self, Key, value): "" "Inserts a new Keynode<key, value> with Freq 1. : Type Key:str:rtype:void "" "Keynode = self.keydict[key] = Keynode (key, value) Freqnode = Self.freqDict.get (1) if freqnode is None:freqnode = Self.freqdic T[1] = Freqnode (1, None, self.head) If Self.head:self.head.prev = Freqnode self.head = Freqnode Self.linkkey (Keynode, Freqnode) def delfreqnode (self, Freqnode): "" "Delete Freqnode. : Rtype:void "" "prev, next = Freqnode.prev, freqnode.next if Prev:prev.next = Next If Next:next.prev = prev if Self.head = = FreqNode:self.head = Next del self.freqdict[freqnode.freq] def i Nsertfreqnodeafter (self, Freq, node): "" "Inserts a new Freqnode (Freq) after node. : Rtype:freqnode "" "NewNode = Freqnode (freq, node, node.next) Self.freqdict[freq] = NewNode I F Node.next:node.next.prev = NewNode Node.next = newNode return NewNode def removekeynode (self, Keynode) : "" "Remove Keynode:Rtype:void "" "Self.unlinkkey (Keynode, Self.freqdict[keynode.freq]) del Self.keydict[keynode.key] def unlinkkey (self, Keynode, Freqnode): "" "Unlink Keynode from FreqNode:rtype:void" " Next, prev = Keynode.next, keynode.prev if Prev:prev.next = next if Next:next.prev = prev If fr Eqnode.first = = KeyNode:freqNode.first = Next if Freqnode.last = = KeyNode:freqNode.last = prev If freqnode . First is None:self.delFreqNode (Freqnode) def linkkey (self, Keynode, Freqnode): "" "Link Keynode to Freq Node:rtype:void "" "Firstkeynode = Freqnode.first Keynode.prev = None Keynode.next = Firstkeynode if FirstKeyNode:firstKeyNode.prev = Keynode Freqnode.first = Keynode if Freqnode.last i s None:freqNode.last = keynode# Your Lfucache object would be instantiated and called as such:# obj = Lfucache (capacity) # Param_1 = Obj.get (key) #Obj.set (Key,value)
Python:
Class Cachenode (object): Def __init__ (self, key, value, Freq_node, Pre, NXT): Self.key = key Self.value = Value Self.freq_node = Freq_node Self.pre = Pre # previous Cachenode self.nxt = nxt # next Cachenode def free_myself (self): if Self.freq_node.cache_head = = SELF.FREQ_NODE.CACHE_TAIL:SELF.FREQ_NODE.CAC He_head = Self.freq_node.cache_tail = None elif Self.freq_node.cache_head = = Self:self.nxt.pre = None Self.freq_node.cache_head = self.nxt elif Self.freq_node.cache_tail = = SELF:SELF.PRE.NXT = No Ne self.freq_node.cache_tail = self.pre else:self.pre.nxt = self.nxt self.nxt.pre = Self.pre Self.pre = None Self.nxt = None Self.freq_node = Noneclass Freqnode (object): Def __ini T__ (self, freq, Pre, NXT): Self.freq = freq Self.pre = Pre # previous Freqnode self.nxt = NXT # Next Freqnode SELF.CACHe_head = none # Cachenode head under this freqnode self.cache_tail = None # Cachenode tail under this Freqnode def count_caches (self): If self.cache_head are None and Self.cache_tail is None:return 0 elif sel F.cache_head = = Self.cache_tail:return 1 Else:return ' "def" Remove (self): if SE Lf.pre is not None:self.pre.nxt = SELF.NXT if self.nxt are not None:self.nxt.pre = Self.pre Pre = Self.pre NXT = self.nxt Self.pre = self.nxt = Self.cache_head = Self.cache_tail = None r Eturn (Pre, NXT) def pop_head_cache (self): If self.cache_head are None and Self.cache_tail is None:re Turn None elif Self.cache_head = = Self.cache_tail:cache_head = Self.cache_head self.cache_he AD = Self.cache_tail = None return Cache_head else:cache_head = Self.cache_head s Elf.cache_head.nxt.pre = None self.cache_head = self.cache_head.nxt return cache_head def append_cache_to_tail (self, Cach E_node): Cache_node.freq_node = self if self.cache_head are None and Self.cache_tail is none:self . Cache_head = Self.cache_tail = Cache_node Else:cache_node.pre = Self.cache_tail Cache_node. NXT = None SELF.CACHE_TAIL.NXT = Cache_node Self.cache_tail = Cache_node def insert_after_me (self , freq_node): Freq_node.pre = self FREQ_NODE.NXT = SELF.NXT If self.nxt are not none:self. Nxt.pre = Freq_node self.nxt = Freq_node def insert_before_me (self, freq_node): If Self.pre is no T None:self.pre.nxt = freq_node Freq_node.pre = self.pre freq_node.nxt = Self sel F.pre = Freq_node class Lfucache (object): Def __init__ (self, capacity): Self.cache = {} # {Key:cache_nod e} self.capacity= Capacity Self.freq_link_head = None def get (self, key): If key in Self.cache:cache_node = Self.cache[key] Freq_node = cache_node.freq_node value = Cache_node.value Self.move_fo Rward (Cache_node, Freq_node) return value else:return-1 def set (self, key, value): If self.capacity <= 0:return-1 if key not in Self.cache:if Len (self.cache) &G t;= Self.capacity:self.dump_cache () Self.create_cache (key, value) Else:cache _node = Self.cache[key] Freq_node = Cache_node.freq_node Cache_node.value = value Self.mo Ve_forward (Cache_node, Freq_node) def move_forward (self, Cache_node, Freq_node): If FREQ_NODE.NXT is None or fre Q_node.nxt.freq! = freq_node.freq + 1:target_freq_node = Freqnode (Freq_node.freq + 1, none, none) t Arget_empty = True Else:target_freq_node = freq_node.nxt Target_empty = False cache_node.free_myself ( ) Target_freq_node.append_cache_to_tail (Cache_node) if Target_empty:freq_node.insert_after_me (Ta Rget_freq_node) if freq_node.count_caches () = = 0:if Self.freq_link_head = = Freq_node:se Lf.freq_link_head = Target_freq_node freq_node.remove () def dump_cache (self): Head_freq_node = Self.f Req_link_head Self.cache.pop (Head_freq_node.cache_head.key) Head_freq_node.pop_head_cache () if Head_f Req_node.count_caches () = = 0:self.freq_link_head = Head_freq_node.nxt head_freq_node.remove () de F Create_cache (self, Key, value): Cache_node = Cachenode (key, value, none, none, none) self.cache[key] = CAC He_node if Self.freq_link_head is None or self.freq_link_head.freq! = 0:new_freq_node = Freqnod E (0, none, none) New_freq_node.append_cache_to_tail (Cache_node) If Self.freq_link_head is not None:self.freq _link_head.insert_before_me (new_freq_node) Self.freq_link_head = New_freq_node Else: Self.freq_link_head.append_cache_to_tail (Cache_node)
C + +:
Class Lfucache {public:lfucache (int capacity) {cap = capacity; } int get (int key) {if (M.count (key) = = 0) return-1; Freq[m[key].second].erase (Iter[key]); ++m[key].second; Freq[m[key].second].push_back (key); Iter[key] =--freq[m[key].second].end (); if (freq[minfreq].size () = = 0) ++minfreq; return m[key].first; } void put (int key, int value) {if (Cap <= 0) return; if (Get (key)! =-1) {M[key].first = value; Return } if (M.size () >= cap) {m.erase (Freq[minfreq].front ()); Iter.erase (Freq[minfreq].front ()); Freq[minfreq].pop_front (); } M[key] = {value, 1}; Freq[1].push_back (key); Iter[key] =--freq[1].end (); Minfreq = 1; }private:int cap, Minfreq; Unordered_map<int, Pair<int, int>> m; Unordered_map<int, list<int>> freq; Unordered_map<int, List<int>::iterator> iter;};
[Leetcode] 146. LRU Cache uses least recent caches
[Leetcode] 460. LFU cache Most infrequently used page substitution buffers