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:
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 = 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:
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++:
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 题目汇总