-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path460. LFU Cache.cpp
More file actions
163 lines (139 loc) · 4.18 KB
/
460. LFU Cache.cpp
File metadata and controls
163 lines (139 loc) · 4.18 KB
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
#include <set>
#include <unordered_map>
using namespace std;
// T: get & put O(logn) because of insert & erase operations of bst
class LFUCache1 {
public:
LFUCache1(int capacity) {
capacity_ = capacity;
tick_ = 0;
}
int get(int key) {
auto it = m_.find(key);
if (it == m_.end()) return -1;
// found key
touch(it->second);
return it->second.value;
}
void put(int key, int value) {
if (capacity_ == 0) return;
auto it = m_.find(key);
if (it != m_.end()) {
it->second.value = value;
touch(it->second);
// m_[key] = it->second;
return;
}
// Not found.
if (cache_.size() == capacity_) {
// erase the first node which has the smallest freq
auto node = cache_.cbegin();
// don't forget to erase the node from the map
m_.erase(node->key);
cache_.erase(*node);
}
// Now the cache has enough space
Node node{key, value, 1, ++tick_};
cache_.insert(node);
m_[key] = node;
}
private:
int capacity_;
int tick_;
struct Node {
int key;
int value;
int freq;
int tick;
bool operator<(const Node& node) const {
if (freq < node.freq) return true;
if (freq == node.freq) return tick < node.tick;
return false;
}
};
// hash table
unordered_map<int, Node> m_;
// balanced search tree(red-black tree), insert & evict both in O(logn) time
set<Node> cache_;
// changes the status and re-balancing
void touch(Node& node) {
cache_.erase(node);
node.freq++;
node.tick = ++tick_;
cache_.insert(node);
}
};
// T: get & put O(1) using doubly linked list
class LFUCache2 {
public:
LFUCache2(int capacity) {
capacity_ = capacity;
min_freq_ = 0;
}
int get(int key) {
if (capacity_ == 0) return -1;
auto it = m_.find(key);
if (it == m_.end()) return -1;
// found key
touch(it->second);
return it->second.value;
}
void put(int key, int value) {
if (capacity_ == 0) return;
auto it = m_.find(key);
if (it != m_.end()) {
it->second.value = value;
touch(it->second);
return;
}
// New key
// ensures there is enough space
if (m_.size() == capacity_) {
m_.erase(l_[min_freq_].back());
l_[min_freq_].pop_back();
if (l_[min_freq_].empty()) l_.erase(min_freq_);
}
min_freq_ = 1;
// insert the new key
l_[min_freq_].push_front(key);
// uses 1 not min_freq_ will report memory error
m_[key] = {key, value, min_freq_, l_[min_freq_].begin()};
return;
}
private:
int capacity_;
int min_freq_;
struct CacheNode {
int key;
int value;
int freq;
// pointer to the position in the same freq node list
list<int>::iterator it;
};
// freq -> same freq keys
unordered_map<int, list<int>> l_;
// key -> CacheNode including the value, freq and the position in the same freq key list(stored in l_)
unordered_map<int, CacheNode> m_;
// nodes are registered before entering into touch
void touch(CacheNode& node) {
int pre_freq = node.freq;
node.freq++;
// removes from the old freq list
l_[pre_freq].erase(node.it);
// only when pre_freq == min_freq, the list needs to be deleted
if (l_[pre_freq].empty() && pre_freq == min_freq_) {
l_.erase(pre_freq);
min_freq_++;
}
// inserts to new freq list
l_[node.freq].push_front(node.key);
// updates the key cachenode map
m_[node.key].it = l_[node.freq].begin();
}
};
/**
* Your LFUCache object will be instantiated and called as such:
* LFUCache* obj = new LFUCache(capacity);
* int param_1 = obj->get(key);
* obj->put(key,value);
*/