-
Notifications
You must be signed in to change notification settings - Fork 656
/
Copy pathLFUCacheJava
162 lines (145 loc) · 5.44 KB
/
LFUCacheJava
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
class LFUCache {
final int capacity;
int curSize;
int minFrequency;
Map<Integer, DLLNode> cache;
Map<Integer, DoubleLinkedList> frequencyMap;
/*.*/
/*
* @param capacity: total capacity of LFU Cache
* @param curSize: current size of LFU cache
* @param minFrequency: frequency of the last linked list (the minimum frequency of entire LFU cache)
* @param cache: a hash map that has key to Node mapping, which used for storing all nodes by their keys
* @param frequencyMap: a hash map that has key to linked list mapping, which used for storing all
* double linked list by their frequencies
* */
public LFUCache(int capacity) {
this.capacity = capacity;
this.curSize = 0;
this.minFrequency = 0;
this.cache = new HashMap<>();
this.frequencyMap = new HashMap<>();
}
/** get node value by key, and then update node frequency as well as relocate that node **/
public int get(int key) {
DLLNode curNode = cache.get(key);
if (curNode == null) {
return -1;
}
updateNode(curNode);
return curNode.val;
}
/**
* add new node into LFU cache, as well as double linked list
* condition 1: if LFU cache has input key, update node value and node position in list
* condition 2: if LFU cache does NOT have input key
* - sub condition 1: if LFU cache does NOT have enough space, remove the Least Recent Used node
* in minimum frequency list, then add new node
* - sub condition 2: if LFU cache has enough space, add new node directly
* **/
public void put(int key, int value) {
// corner case: check cache capacity initialization
if (capacity == 0) {
return;
}
if (cache.containsKey(key)) {
DLLNode curNode = cache.get(key);
curNode.val = value;
updateNode(curNode);
}
else {
curSize++;
if (curSize > capacity) {
// get minimum frequency list
DoubleLinkedList minFreqList = frequencyMap.get(minFrequency);
cache.remove(minFreqList.tail.prev.key);
minFreqList.removeNode(minFreqList.tail.prev);
curSize--;
}
// reset min frequency to 1 because of adding new node
minFrequency = 1;
DLLNode newNode = new DLLNode(key, value);
// get the list with frequency 1, and then add new node into the list, as well as into LFU cache
DoubleLinkedList curList = frequencyMap.getOrDefault(1, new DoubleLinkedList());
curList.addNode(newNode);
frequencyMap.put(1, curList);
cache.put(key, newNode);
}
}
public void updateNode(DLLNode curNode) {
int curFreq = curNode.frequency;
DoubleLinkedList curList = frequencyMap.get(curFreq);
curList.removeNode(curNode);
// if current list the the last list which has lowest frequency and current node is the only node in that list
// we need to remove the entire list and then increase min frequency value by 1
if (curFreq == minFrequency && curList.listSize == 0) {
minFrequency++;
}
curNode.frequency++;
// add current node to another list has current frequency + 1,
// if we do not have the list with this frequency, initialize it
DoubleLinkedList newList = frequencyMap.getOrDefault(curNode.frequency, new DoubleLinkedList());
newList.addNode(curNode);
frequencyMap.put(curNode.frequency, newList);
}
/*
* @param key: node key
* @param val: node value
* @param frequency: frequency count of current node
* (all nodes connected in same double linked list has same frequency)
* @param prev: previous pointer of current node
* @param next: next pointer of current node
* */
class DLLNode {
int key;
int val;
int frequency;
DLLNode prev;
DLLNode next;
public DLLNode(int key, int val) {
this.key = key;
this.val = val;
this.frequency = 1;
}
}
/*
* @param listSize: current size of double linked list
* @param head: head node of double linked list
* @param tail: tail node of double linked list
* */
class DoubleLinkedList {
int listSize;
DLLNode head;
DLLNode tail;
public DoubleLinkedList() {
this.listSize = 0;
this.head = new DLLNode(0, 0);
this.tail = new DLLNode(0, 0);
head.next = tail;
tail.prev = head;
}
/** add new node into head of list and increase list size by 1 **/
public void addNode(DLLNode curNode) {
DLLNode nextNode = head.next;
curNode.next = nextNode;
curNode.prev = head;
head.next = curNode;
nextNode.prev = curNode;
listSize++;
}
/** remove input node and decrease list size by 1**/
public void removeNode(DLLNode curNode) {
DLLNode prevNode = curNode.prev;
DLLNode nextNode = curNode.next;
prevNode.next = nextNode;
nextNode.prev = prevNode;
listSize--;
}
}
}
/**
* 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);
*/