forked from shuboc/LeetCode-2
-
Notifications
You must be signed in to change notification settings - Fork 15
/
lfu-cache.py
135 lines (113 loc) · 4.02 KB
/
lfu-cache.py
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
# Time: O(1), per operation
# Space: O(k), k is the capacity of 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
import collections
class ListNode(object):
def __init__(self, key, value, freq):
self.key = key
self.val = value
self.freq = freq
self.next = None
self.prev = None
class LinkedList(object):
def __init__(self):
self.head = None
self.tail = None
def append(self, node):
node.next, node.prev = None, None # avoid dirty node
if self.head is None:
self.head = node
else:
self.tail.next = node
node.prev = self.tail
self.tail = node
def delete(self, node):
if node.prev:
node.prev.next = node.next
else:
self.head = node.next
if node.next:
node.next.prev = node.prev
else:
self.tail = node.prev
node.next, node.prev = None, None # make node clean
class LFUCache(object):
def __init__(self, capacity):
"""
:type capacity: int
"""
self.__capa = capacity
self.__size = 0
self.__min_freq = 0
self.__freq_to_nodes = collections.defaultdict(LinkedList)
self.__key_to_node = {}
def get(self, key):
"""
:type key: int
:rtype: int
"""
if key not in self.__key_to_node:
return -1
old_node = self.__key_to_node[key]
self.__key_to_node[key] = ListNode(key, old_node.val, old_node.freq)
self.__freq_to_nodes[old_node.freq].delete(old_node)
if not self.__freq_to_nodes[self.__key_to_node[key].freq].head:
del self.__freq_to_nodes[self.__key_to_node[key].freq]
if self.__min_freq == self.__key_to_node[key].freq:
self.__min_freq += 1
self.__key_to_node[key].freq += 1
self.__freq_to_nodes[self.__key_to_node[key].freq].append(self.__key_to_node[key])
return self.__key_to_node[key].val
def put(self, key, value):
"""
:type key: int
:type value: int
:rtype: void
"""
if self.__capa <= 0:
return
if self.get(key) != -1:
self.__key_to_node[key].val = value
return
if self.__size == self.__capa:
del self.__key_to_node[self.__freq_to_nodes[self.__min_freq].head.key]
self.__freq_to_nodes[self.__min_freq].delete(self.__freq_to_nodes[self.__min_freq].head)
if not self.__freq_to_nodes[self.__min_freq].head:
del self.__freq_to_nodes[self.__min_freq]
self.__size -= 1
self.__min_freq = 1
self.__key_to_node[key] = ListNode(key, value, self.__min_freq)
self.__freq_to_nodes[self.__key_to_node[key].freq].append(self.__key_to_node[key])
self.__size += 1
# Your LFUCache object will be instantiated and called as such:
# obj = LFUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)