[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:
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 题目汇总
[LeetCode] 460. LFU Cache 最近最不常用页面置换缓存器的更多相关文章
- [LeetCode] LFU Cache 最近最不常用页面置换缓存器
Design and implement a data structure for Least Frequently Used (LFU) cache. It should support the f ...
- [LeetCode] LRU Cache 最近最少使用页面置换缓存器
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the fol ...
- [LeetCode] 146. LRU Cache 最近最少使用页面置换缓存器
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the fol ...
- leetcode 460. LFU Cache
hash:存储的key.value.freq freq:存储的freq.key,也就是说出现1次的所有key在一起,用list连接 class LFUCache { public: LFUCache( ...
- leetcode 146. LRU Cache 、460. LFU Cache
LRU算法是首先淘汰最长时间未被使用的页面,而LFU是先淘汰一定时间内被访问次数最少的页面,如果存在使用频度相同的多个项目,则移除最近最少使用(Least Recently Used)的项目. LFU ...
- [LeetCode]460.LFU缓存机制
设计并实现最不经常使用(LFU)缓存的数据结构.它应该支持以下操作:get 和 put. get(key) - 如果键存在于缓存中,则获取键的值(总是正数),否则返回 -1.put(key, valu ...
- (待续)C#语言中的动态数组(ArrayList)模拟常用页面置换算法(FIFO、LRU、Optimal)
目录 00 简介 01 算法概述 02 公用方法与变量解释 03 先进先出置换算法(FIFO) 04 最近最久未使用(LRU)算法 05 最佳置换算法(OPT) 00 简介 页面置换算法主要是记录内存 ...
- [LeetCode] 146. LRU Cache 近期最少使用缓存
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the fol ...
- Leetcode: LFU Cache && Summary of various Sets: HashSet, TreeSet, LinkedHashSet
Design and implement a data structure for Least Frequently Used (LFU) cache. It should support the f ...
随机推荐
- @Scope("prototype")
spring中bean的scope属性,有如下5种类型: singleton 表示在spring容器中的单例,通过spring容器获得该bean时总是返回唯一的实例prototype表示每次获得bea ...
- vs code 搭建java maven springboot环境
Java Extension Pack,Maven for Java,Spring Boot Extension Pack https://blog.csdn.net/qq_26026975/arti ...
- JQuery系列(3) - 工具方法
jQuery函数库提供了一个jQuery对象(简写为$),这个对象本身是一个构造函数,可以用来生成jQuery对象的实例.有了实例以后,就可以调用许多针对实例的方法,它们定义jQuery.protot ...
- if语句的嵌套:从键盘输入3个实数,利用条件表达式求其最大者。
#include<stdio.h>void main(){ float a,b,c,max; scanf("%f%f%f",&a,&b,&c); ...
- 洛谷 P2004 领地选择 题解
P2004 领地选择 题目描述 作为在虚拟世界里统帅千军万马的领袖,小Z认为天时.地利.人和三者是缺一不可的,所以,谨慎地选择首都的位置对于小T来说是非常重要的. 首都被认为是一个占地C*C的正方形. ...
- 计蒜客 39268.Tasks-签到 (The 2019 ACM-ICPC China Shannxi Provincial Programming Contest A.) 2019ICPC西安邀请赛现场赛重现赛
Tasks It's too late now, but you still have too much work to do. There are nn tasks on your list. Th ...
- (13)Go接口
接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节. 接口 接口类型 在Go语言中接口(interface)是一种类型,一种抽象的类型. interfa ...
- Java实现RS485串口通信,发送和接收数据进行解析
最近项目有一个空气检测仪,需要得到空气检测仪的实时数据,保存到数据库当中.根据了解得到,硬件是通过rs485进行串口通讯的,需要发送16进制命令给仪器,然后通过轮询来得到数据. 需要先要下载RXTX的 ...
- MySQL的ROUND函数
ROUND(X) ROUND(X,D) 返回参数X, 其值接近于最近似的整数.在有两个参数的情况下,返回 X ,其值保留到小数点后D位,而第D位的保留方式为四舍五入.若要接保留X值小数点左边的D 位, ...
- 记录python循环引用带来的MemoryError错误解决
在以前的python中,没有遇到过这样的错误,上来之后还是很蒙蔽的,问了几个业内的人,他们 都给我说让我改服务器里面配置的东西, 因为是要给大家用的,服务器要保证长久运行,不能临时去修改这个,导致在大 ...