Skip to content

Commit d7eccd7

Browse files
committed
Add LRU Cache implementation
1 parent f31f749 commit d7eccd7

File tree

5 files changed

+256
-0
lines changed

5 files changed

+256
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ List of Programs related to data structures and algorithms
198198
| 6 | Longest consequtive sequence |[Source](https://github.com/sudheerj/datastructures-algorithms/blob/master/src/javascript/algorithms/hashtable/6.longestConsecutiveSequence/longestConsecutiveSequence.js) | [JavaScript](https://livecodes.io/?console&x=https://github.com/sudheerj/datastructures-algorithms/blob/master/src/javascript/algorithms/hashtable/6.longestConsecutiveSequence/longestConsecutiveSequence.js) | [Documentation](https://github.com/sudheerj/datastructures-algorithms/blob/master/src/javascript/algorithms/hashtable/6.longestConsecutiveSequence/longestConsecutiveSequence.md) | Medium | Find sequence using set or hash table |
199199
| 7 | Valid Sudoku | [Source](https://github.com/sudheerj/datastructures-algorithms/blob/master/src/javascript/algorithms/hashtable/7.validSudoku/validSudoku.js) | [JavaScript](https://livecodes.io/?console&x=https://github.com/sudheerj/datastructures-algorithms/blob/master/src/javascript/algorithms/hashtable/7.validSudoku/validSudoku.js) | [Documentation](https://github.com/sudheerj/datastructures-algorithms/blob/master/src/javascript/algorithms/hashtable/7.validSudoku/validSudoku.md) | Medium | Using Map and Set methods |
200200
| 8 | Letter combinations | [Source](https://github.com/sudheerj/datastructures-algorithms/blob/master/src/javascript/algorithms/hashtable/8.letterCombinations/letterCombinations.js) | [JavaScript](https://livecodes.io/?console&x=https://github.com/sudheerj/datastructures-algorithms/blob/master/src/javascript/algorithms/hashtable/8.letterCombinations/letterCombinations.js) | [Documentation](https://github.com/sudheerj/datastructures-algorithms/blob/master/src/javascript/algorithms/hashtable/8.letterCombinations/letterCombinations.md) | Medium | Backtracking with hash table mapping |
201+
| 9 | LRU Cache | [Source](https://github.com/sudheerj/datastructures-algorithms/blob/master/src/javascript/algorithms/hashtable/9.lruCache/lruCache.js) | [JavaScript](https://livecodes.io/?console&x=https://github.com/sudheerj/datastructures-algorithms/blob/master/src/javascript/algorithms/hashtable/9.lruCache/lruCache.js) | [Documentation](https://github.com/sudheerj/datastructures-algorithms/blob/master/src/javascript/algorithms/hashtable/9.lruCache/lruCache.md) | Medium | Combination of Hash Table and doubly linked list |
201202

202203
## Sorting
203204

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package java1.algorithms.hashmap.lruCache;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
6+
class Node {
7+
Integer key;
8+
Integer value;
9+
Node next;
10+
Node prev;
11+
12+
Node(Integer key, Integer value) {
13+
this.key = key;
14+
this.value = value;
15+
}
16+
}
17+
18+
public class LRUCache {
19+
int capacity;
20+
Map<Integer, Node> cache;
21+
Node head;
22+
Node tail;
23+
24+
LRUCache(int capacity){
25+
this.capacity = capacity;
26+
this.cache = new HashMap<>();
27+
this.head = new Node(null, null);
28+
this.tail = new Node(null, null);
29+
this.head.next = this.tail;
30+
this.tail.prev = this.head;
31+
}
32+
33+
public int get(int key){
34+
if(this.cache.containsKey(key)) {
35+
Node node = this.cache.get(key);
36+
this._remove(node);
37+
this._insert(node);
38+
return node.value;
39+
}
40+
41+
return -1;
42+
}
43+
44+
public void set(int key, int value){
45+
if(this.cache.containsKey(key)) {
46+
this._remove(this.cache.get(key));
47+
}
48+
49+
Node node = new Node(key, value);
50+
this._insert(node);
51+
this.cache.put(key, node);
52+
53+
if(this.cache.size() > this.capacity) {
54+
Node lruNode = this.tail.prev;
55+
this._remove(lruNode);
56+
this.cache.remove(lruNode.key);
57+
}
58+
}
59+
60+
private void _remove(Node node){
61+
node.prev.next = node.next;
62+
node.next.prev = node.prev;
63+
}
64+
65+
private void _insert(Node node) {
66+
node.next = this.head.next;
67+
node.prev = this.head;
68+
this.head.next.prev = node;
69+
this.head.next = node;
70+
}
71+
72+
public static void main(String[] args) {
73+
LRUCache lruCache = new LRUCache(2);
74+
lruCache.set(1, 5);
75+
System.out.println(lruCache.get(1));
76+
lruCache.set(2, 10);
77+
lruCache.set(3, 15);
78+
System.out.println(lruCache.get(2));
79+
lruCache.set(4, 20);
80+
System.out.println(lruCache.get(1));
81+
}
82+
}
83+
84+
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
**Problem statement:**
2+
Implement the **Least Recently Used (LRU) cache** class `LRUCache` with the following operations,
3+
1. `LRUCache(int capacity)` Initialize the LRU cache of positive size `capacity`.
4+
2. `int get(int key)` Return the value of the `key` if the `key` exists, otherwise return -1.
5+
3. `void put(int key, int value)` Update the value of the `key` if the `key` exists. Otherwise, add the key-value pair to the cache. If the addition of the new key-value pair causes the cache to exceed its capacity, remove the least recently used key.
6+
7+
8+
**Note:** Each `get` and `put` needs to run in `O(1)` average time complexity.
9+
10+
![Screenshot](../../../../images/lruCache.png)
11+
12+
## Examples:
13+
Example 1:
14+
15+
Input:
16+
["LRUCache", [2], "put", [1, 5], "get", [1], "put", [2, 10], "put", [3, 15], "get", [2], "put", [4,20], "get", [1]]
17+
18+
Output:
19+
[null, null, 5, null, null, 10, null, null, -1]
20+
21+
Explanation:
22+
LRUCache lRUCache = new LRUCache(2);
23+
lRUCache.put(1, 5); // cache: {1=5}
24+
lRUCache.get(1); // return 5
25+
lRUCache.put(2, 10); // cache: {1=5, 2=10}
26+
lRUCache.put(3, 15); // cache: {2=10, 3=15}, key=1 was evicted
27+
lRUCache.get(2); // returns 10
28+
lRUCache.put(4, 20); // cache: {3=15, 4=20}, key=2 was evicted
29+
lRUCache.get(1); // return -1 (not found)
30+
31+
32+
**Algorithmic Steps**
33+
This problem is solved with the help of constant time lookups through hash map and ordered structured of doubly linked list data strcture. The hash map can provide get and put operations with `O(1)` time complexity, which is essential for LRU cache system. The algorithmic approach can be summarized as follows:
34+
35+
1. Create a node class(`Node`) to represent each entry in the cache. Each node will have a key, value, and pointers to the previous and next node.
36+
37+
2. Create a `LRUCache` class to manage the nodes and implementing the LRU Cache mechanism.
38+
39+
3. The LRUCache class contains capacity of cache, cache hash map, and two dummy pointers named as head and tail nodes.
40+
41+
4. Initially head and tail nodes connected each other without any real nodes in between them.
42+
43+
5. Create two internal methods `_remove` and `_insert` to use them with in get and set operations of LRU Cache. The remove method deletes the node from the doubly linked list. Whereas insert method is used to add a new node next to the head node.
44+
45+
6. Implement `get` method to fetch the node value from LRU cache. If the given key exists in cache, return the node value and increase the priority of the data in the LRU cache. Otherwise, return -1.
46+
47+
7. Implement `set` method to add key-value pair as an entry inside LRU cache. If the given key already exists in cache, update its value in the cache. Otherwise, add a key-value pair to the cache. Incase the number of keys exceeds the capacity of the LRU cache, evict the least recently used data.
48+
49+
**Time and Space complexity:**
50+
This algorithm has a time complexity of `O(1)` for both get and set methods. This is because accessing an item from hash map and updating its position in doubly linked list takes `O(1)` time complexity. In the same way, inserting an item inside hash map and adjusting the double linked list requires `O(1)` time complexity.
51+
52+
Here, both hash map and double linked list requires `O(n)` space to store all the items in cache, where `n` is the capacity of cache.
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
class Node {
2+
constructor(key, value){
3+
this.key = key;
4+
this.value = value;
5+
this.head = null;
6+
this.tail = null;
7+
}
8+
}
9+
10+
class LRUCache {
11+
constructor(capacity){
12+
this.capacity = capacity;
13+
this.cache = new Map();
14+
this.head = new Node(null, null);
15+
this.tail = new Node(null, null);
16+
this.head.next = this.tail;
17+
this.tail.prev = this.head;
18+
}
19+
20+
get(key){
21+
if(this.cache.has(key)) {
22+
const node = this.cache.get(key);
23+
this._remove(node);
24+
this._insert(node);
25+
return node.value;
26+
}
27+
28+
return -1;
29+
}
30+
31+
put(key, value) {
32+
if(this.cache.has(key)) {
33+
this._remove(this.cache.get(key));
34+
}
35+
36+
const node = new Node(key, value);
37+
this._insert(node);
38+
this.cache.set(key, node);
39+
40+
if(this.cache.size > this.capacity){
41+
const lruNode = this.tail.prev;
42+
this._remove(lruNode);
43+
this.cache.delete(lruNode.key);
44+
}
45+
}
46+
47+
_remove(node){
48+
node.prev.next = node.next;
49+
node.next.prev = node.prev;
50+
}
51+
52+
_insert(node) {
53+
node.next = this.head.next;
54+
node.prev = this.head;
55+
this.head.next.prev = node;
56+
this.head.next = node;
57+
}
58+
}
59+
60+
let lruCache = new LRUCache(2);
61+
lruCache.put(1, 5);
62+
console.log(lruCache.get(1));
63+
lruCache.put(2, 10);
64+
lruCache.put(3, 15);
65+
console.log(lruCache.get(2));
66+
lruCache.put(4, 20);
67+
console.log(lruCache.get(1));
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
**Problem statement:**
2+
Implement the **Least Recently Used (LRU) cache** class `LRUCache` with the following operations,
3+
1. `LRUCache(int capacity)` Initialize the LRU cache of positive size `capacity`.
4+
2. `int get(int key)` Return the value of the `key` if the `key` exists, otherwise return -1.
5+
3. `void put(int key, int value)` Update the value of the `key` if the `key` exists. Otherwise, add the key-value pair to the cache. If the addition of the new key-value pair causes the cache to exceed its capacity, remove the least recently used key.
6+
7+
8+
**Note:** Each `get` and `put` needs to run in `O(1)` average time complexity.
9+
10+
![Screenshot](../../../../images/lruCache.png)
11+
12+
## Examples:
13+
Example 1:
14+
15+
Input:
16+
["LRUCache", [2], "put", [1, 5], "get", [1], "put", [2, 10], "put", [3, 15], "get", [2], "put", [4,20], "get", [1]]
17+
18+
Output:
19+
[null, null, 5, null, null, 10, null, null, -1]
20+
21+
Explanation:
22+
LRUCache lRUCache = new LRUCache(2);
23+
lRUCache.put(1, 5); // cache: {1=5}
24+
lRUCache.get(1); // return 5
25+
lRUCache.put(2, 10); // cache: {1=5, 2=10}
26+
lRUCache.put(3, 15); // cache: {2=10, 3=15}, key=1 was evicted
27+
lRUCache.get(2); // returns 10
28+
lRUCache.put(4, 20); // cache: {3=15, 4=20}, key=2 was evicted
29+
lRUCache.get(1); // return -1 (not found)
30+
31+
32+
**Algorithmic Steps**
33+
This problem is solved with the help of constant time lookups through hash map and ordered structured of doubly linked list data strcture. The hash map can provide get and put operations with `O(1)` time complexity, which is essential for LRU cache system. The algorithmic approach can be summarized as follows:
34+
35+
1. Create a node class(`Node`) to represent each entry in the cache. Each node will have a key, value, and pointers to the previous and next node.
36+
37+
2. Create a `LRUCache` class to manage the nodes and implementing the LRU Cache mechanism.
38+
39+
3. The LRUCache class contains capacity of cache, cache hash map, and two dummy pointers named as head and tail nodes.
40+
41+
4. Initially head and tail nodes connected each other without any real nodes in between them.
42+
43+
5. Create two internal methods `_remove` and `_insert` to use them with in get and set operations of LRU Cache. The remove method deletes the node from the doubly linked list. Whereas insert method is used to add a new node next to the head node.
44+
45+
6. Implement `get` method to fetch the node value from LRU cache. If the given key exists in cache, return the node value and increase the priority of the data in the LRU cache. Otherwise, return -1.
46+
47+
7. Implement `set` method to add key-value pair as an entry inside LRU cache. If the given key already exists in cache, update its value in the cache. Otherwise, add a key-value pair to the cache. Incase the number of keys exceeds the capacity of the LRU cache, evict the least recently used data.
48+
49+
**Time and Space complexity:**
50+
This algorithm has a time complexity of `O(1)` for both get and set methods. This is because accessing an item from hash map and updating its position in doubly linked list takes `O(1)` time complexity. In the same way, inserting an item inside hash map and adjusting the double linked list requires `O(1)` time complexity.
51+
52+
Here, both hash map and double linked list requires `O(n)` space to store all the items in cache, where `n` is the capacity of cache.

0 commit comments

Comments
 (0)