[LeetCode] 460. LFU Cache 最近最不常用页面置换缓存器
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 (will 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 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., two or more keys that have the same frequency), the least recently used key would be evicted.
Follow up:
Could you 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 1 cache.put(3, 3); // evicts key 2 cache.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 3 cache.get(4); // returns 4
双向链表(Doubly Linked List) + 哈希表(Hash Table)
Java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | class KeyNode( object ): def __init__( self , key, value, freq = 1 ): self .key = key self .value = value self .freq = freq self .prev = self . next = None class FreqNode( object ): def __init__( self , freq, prev, next ): self .freq = freq self .prev = prev self . next = next self .first = self .last = None class LFUCache( object ): def __init__( self , capacity): """ :type capacity: int """ self .capacity = capacity 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( self , 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 freqNode = self .freqDict[keyNode.freq] nextFreqNode = freqNode. next keyNode.freq + = 1 if nextFreqNode is None 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 .freqDict[ 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 insertFreqNodeAfter( self , freq, node): """ Insert a new FreqNode(freq) after node. :rtype: FreqNode """ newNode = FreqNode(freq, node, node. next ) self .freqDict[freq] = newNode if 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 freqNode.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 freqNode :rtype: void """ firstKeyNode = freqNode.first keyNode.prev = None keyNode. next = firstKeyNode if firstKeyNode: firstKeyNode.prev = keyNode freqNode.first = keyNode if freqNode.last is None : freqNode.last = keyNode # Your LFUCache object will be instantiated and called as such: # obj = LFUCache(capacity) # param_1 = obj.get(key) # obj.set(key,value) |
Python:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | 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.cache_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 = None 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 = None class FreqNode( object ): def __init__( 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 is None and self .cache_tail is None : return 0 elif self .cache_head = = self .cache_tail: return 1 else : return '2+' def remove( self ): if self .pre is not None : self .pre.nxt = self .nxt if self .nxt is not None : self .nxt.pre = self .pre pre = self .pre nxt = self .nxt self .pre = self .nxt = self .cache_head = self .cache_tail = None return (pre, nxt) def pop_head_cache( self ): if self .cache_head is None and self .cache_tail is None : return None elif self .cache_head = = self .cache_tail: cache_head = self .cache_head self .cache_head = self .cache_tail = None return cache_head else : cache_head = self .cache_head self .cache_head.nxt.pre = None self .cache_head = self .cache_head.nxt return cache_head def append_cache_to_tail( self , cache_node): cache_node.freq_node = self if self .cache_head is 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 is not None : self .nxt.pre = freq_node self .nxt = freq_node def insert_before_me( self , freq_node): if self .pre is not None : self .pre.nxt = freq_node freq_node.pre = self .pre freq_node.nxt = self self .pre = freq_node class LFUCache( object ): def __init__( self , capacity): self .cache = {} # {key: cache_node} 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_forward(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) > = 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 .move_forward(cache_node, freq_node) def move_forward( self , cache_node, freq_node): if freq_node.nxt is None or freq_node.nxt.freq ! = freq_node.freq + 1 : target_freq_node = FreqNode(freq_node.freq + 1 , None , None ) target_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(target_freq_node) if freq_node.count_caches() = = 0 : if self .freq_link_head = = freq_node: self .freq_link_head = target_freq_node freq_node.remove() def dump_cache( self ): head_freq_node = self .freq_link_head self .cache.pop(head_freq_node.cache_head.key) head_freq_node.pop_head_cache() if head_freq_node.count_caches() = = 0 : self .freq_link_head = head_freq_node.nxt head_freq_node.remove() def create_cache( self , key, value): cache_node = CacheNode(key, value, None , None , None ) self .cache[key] = cache_node if self .freq_link_head is None or self .freq_link_head.freq ! = 0 : new_freq_node = FreqNode( 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++:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | 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 近期最少使用缓存
All LeetCode Questions List 题目汇总
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步