-
Notifications
You must be signed in to change notification settings - Fork 0
/
LFUCache.java
175 lines (147 loc) · 5.84 KB
/
LFUCache.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
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
174
175
import java.util.HashMap;
import java.util.Map;
class LFUCache {
// Map to track nodes by frequency. Each frequency maps to a DoubleLinkedList of nodes.
Map<Integer, DoubleLinkedList> frequencyMap;
// Cache to store key-value pairs mapped to their corresponding DLLNode for fast access.
Map<Integer, DLLNode> cache;
// Maximum capacity of the cache.
final int capacity;
// Current size of the cache.
int curSize;
// Minimum frequency of any node in the cache, used for LFU eviction.
int minFrequency;
/**
* Constructor to initialize LFUCache with given capacity.
* @param capacity Maximum capacity of the cache.
*/
public LFUCache(int capacity) {
this.capacity = capacity;
this.frequencyMap = new HashMap<>();
this.cache = new HashMap<>();
this.curSize = 0;
this.minFrequency = 0;
}
/**
* Retrieve the value associated with the given key.
* @param key The key to search for.
* @return The value if the key exists in the cache, otherwise -1.
*/
public int get(int key) {
if (!cache.containsKey(key)) return -1; // Key not found in cache.
DLLNode curNode = cache.get(key);
updateNode(curNode); // Update frequency of the node.
return curNode.value; // Return the value.
}
/**
* Insert a key-value pair into the cache.
* @param key The key to be inserted.
* @param value The value associated with the key.
*/
public void put(int key, int value) {
if (capacity == 0) return; // If cache capacity is zero, do nothing.
if (cache.containsKey(key)) {
// Key exists, update the value and its frequency.
DLLNode curNode = cache.get(key);
curNode.value = value; // Update the value.
updateNode(curNode); // Update the node frequency.
} else {
curSize++; // Increment current cache size.
// If cache exceeds capacity, remove the least frequently used node.
if (curSize > capacity) {
DoubleLinkedList minFreqList = frequencyMap.get(minFrequency);
cache.remove(minFreqList.tail.prev.key); // Remove from cache map.
minFreqList.removeNode(minFreqList.tail.prev); // Remove node from list.
curSize--; // Decrement size.
}
// Add new node to cache.
minFrequency = 1; // Reset minimum frequency to 1.
DLLNode newNode = new DLLNode(key, value);
DoubleLinkedList newList = frequencyMap.getOrDefault(1, new DoubleLinkedList());
newList.addNode(newNode); // Add new node to frequency list.
frequencyMap.put(1, newList); // Update frequency map.
cache.put(key, newNode); // Add to cache map.
}
}
/**
* Update the frequency of a node when it is accessed or updated.
* @param curNode The node whose frequency needs to be updated.
*/
private void updateNode(DLLNode curNode) {
int curFrequency = curNode.frequency; // Get current frequency.
DoubleLinkedList curList = frequencyMap.get(curFrequency);
curList.removeNode(curNode); // Remove node from current frequency list.
// If this was the last node of the current minimum frequency list, update minFrequency.
if (curFrequency == minFrequency && curList.listSize == 0) {
minFrequency++;
}
curNode.frequency++; // Increment node's frequency.
// Add node to the new frequency list.
DoubleLinkedList newList = frequencyMap.getOrDefault(curNode.frequency, new DoubleLinkedList());
newList.addNode(curNode);
frequencyMap.put(curNode.frequency, newList); // Update frequency map.
}
/**
* Class representing a node in the doubly linked list.
*/
class DLLNode {
int key, value, frequency;
DLLNode prev, next;
/**
* Constructor to initialize a node with key and value. Frequency is set to 1.
* @param key The key of the node.
* @param value The value of the node.
*/
public DLLNode(int key, int value) {
this.key = key;
this.value = value;
this.frequency = 1; // Frequency starts at 1.
}
}
/**
* Class representing a doubly linked list.
*/
class DoubleLinkedList {
DLLNode head, tail;
int listSize;
/**
* Constructor to initialize an empty doubly linked list.
*/
public DoubleLinkedList() {
head = new DLLNode(0, 0); // Dummy head node.
tail = new DLLNode(0, 0); // Dummy tail node.
head.next = tail;
tail.prev = head;
listSize = 0; // Initially, the list is empty.
}
/**
* Remove a node from the doubly linked list.
* @param curNode The node to be removed.
*/
public void removeNode(DLLNode curNode) {
DLLNode prevNode = curNode.prev;
DLLNode nextNode = curNode.next;
prevNode.next = nextNode; // Update pointers.
nextNode.prev = prevNode;
listSize--; // Decrement size.
}
/**
* Add a node to the front (most recently used position) of the doubly linked list.
* @param curNode The node to be added.
*/
public void addNode(DLLNode curNode) {
DLLNode nextNode = head.next;
curNode.next = nextNode; // Update pointers.
curNode.prev = head;
head.next = curNode;
nextNode.prev = curNode;
listSize++; // Increment size.
}
}
}
/**
* 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);
*/