Skip to content

Commit 96da5f0

Browse files
committed
Add problem 146 - LRU Cache
1 parent f48452d commit 96da5f0

File tree

4 files changed

+150
-0
lines changed

4 files changed

+150
-0
lines changed

problems/design/__init__.py

Whitespace-only changes.

problems/design/lru__cache.py

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
class LRUCache:
2+
3+
def __init__(self, capacity: int):
4+
# Capacity of the cache
5+
self.capacity = capacity
6+
# Size or total number of entries in the cache
7+
self.size = 0
8+
# Dictionary to store key and entry nodes
9+
self.entries = {}
10+
# Head and tail of the list to store entries
11+
self.head = None
12+
self.tail = None
13+
14+
def get(self, key: int) -> int:
15+
# If the cache already contains the key
16+
if key in self.entries:
17+
# Get the entry corresponding to the key
18+
entry = self.entries[key]
19+
# Delete node from the current position and add it to head
20+
self.delete_entry(entry)
21+
self.update_head(entry)
22+
return entry.value
23+
return -1
24+
25+
def put(self, key: int, value: int) -> None:
26+
# If the key is already in the cache
27+
if key in self.entries:
28+
# Update the value corresponding to the node
29+
entry = self.entries[key]
30+
entry.value = value
31+
# Delete node from its current position and update head
32+
self.delete_entry(entry)
33+
self.update_head(entry)
34+
else:
35+
# Create new entry
36+
entry = self.Entry(key, value)
37+
if self.size >= self.capacity:
38+
self.entries.pop(self.tail.key)
39+
self.delete_entry(self.tail)
40+
self.update_head(entry)
41+
self.entries[key] = entry
42+
self.size += 1
43+
44+
def delete_entry(self, entry):
45+
# If given entry is not the head
46+
if entry.previous is not None:
47+
entry.previous.next = entry.next
48+
else:
49+
self.head = entry.next
50+
# If given entry is not the tail
51+
if entry.next is not None:
52+
entry.next.previous = entry.previous
53+
else:
54+
self.tail = entry.previous
55+
56+
def update_head(self, entry):
57+
# Make entry as the new node
58+
entry.next = self.head
59+
entry.previous = None
60+
if self.head is not None:
61+
self.head.previous = entry
62+
# Update the head
63+
self.head = entry
64+
# If there is only one node
65+
if self.tail is None:
66+
self.tail = entry
67+
68+
class Entry:
69+
def __init__(self, key: int, value: int):
70+
self.key = key
71+
self.value = value
72+
self.previous = None
73+
self.next = None

tests/design/__init__.py

Whitespace-only changes.

tests/design/lru_cache_test.py

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import unittest
2+
3+
from problems.design.lru__cache import LRUCache
4+
5+
6+
class LRUCacheTest(unittest.TestCase):
7+
8+
def test_get_put(self):
9+
cache = LRUCache(2)
10+
11+
# Test cache miss
12+
self.assertEqual(cache.get(1), -1)
13+
14+
# Test cache put and get
15+
cache.put(1, 1)
16+
cache.put(2, 2)
17+
self.assertEqual(cache.get(1), 1)
18+
19+
# Test cache update
20+
cache.put(3, 3)
21+
self.assertEqual(cache.get(2), -1) # 2 should be evicted
22+
self.assertEqual(cache.get(3), 3)
23+
24+
# Test cache eviction
25+
cache.put(4, 4)
26+
self.assertEqual(cache.get(1), -1) # 1 should be evicted
27+
self.assertEqual(cache.get(3), 3)
28+
self.assertEqual(cache.get(4), 4)
29+
30+
def test_lru_order(self):
31+
cache = LRUCache(2)
32+
33+
# Add items
34+
cache.put(1, 1)
35+
cache.put(2, 2)
36+
37+
# Access 1 to make it most recently used
38+
self.assertEqual(cache.get(1), 1)
39+
40+
# Add another item, which should evict 2
41+
cache.put(3, 3)
42+
self.assertEqual(cache.get(2), -1)
43+
self.assertEqual(cache.get(1), 1)
44+
self.assertEqual(cache.get(3), 3)
45+
46+
def test_update_existing_key(self):
47+
cache = LRUCache(2)
48+
49+
# Add and update item
50+
cache.put(1, 1)
51+
cache.put(2, 2)
52+
cache.put(1, 10)
53+
54+
# Ensure value is updated
55+
self.assertEqual(cache.get(1), 10)
56+
57+
# Add another item, which should evict 2 as 1 was updated recently
58+
cache.put(3, 3)
59+
self.assertEqual(cache.get(2), -1)
60+
self.assertEqual(cache.get(1), 10)
61+
self.assertEqual(cache.get(3), 3)
62+
63+
def test_capacity(self):
64+
cache = LRUCache(1)
65+
66+
# Add item and ensure it is accessible
67+
cache.put(1, 1)
68+
self.assertEqual(cache.get(1), 1)
69+
70+
# Add another item, which should evict the first one
71+
cache.put(2, 2)
72+
self.assertEqual(cache.get(1), -1)
73+
self.assertEqual(cache.get(2), 2)
74+
75+
76+
if __name__ == '__main__':
77+
unittest.main()

0 commit comments

Comments
 (0)