|
| 1 | +#Implementing a doubly linked list to reduce complexity of each operation which was previously O(n) to O(1). |
| 2 | +class Node: |
| 3 | + def __init__(self, key, value): |
| 4 | + self.key = key |
| 5 | + self.value = value |
| 6 | + self.prev = None |
| 7 | + self.next = None |
| 8 | + |
| 9 | +class DoublyLinkedList: |
| 10 | + def __init__(self): |
| 11 | + self.head = None # MRU |
| 12 | + self.tail = None # LRU |
| 13 | + |
| 14 | + def push_head(self, node): #accessing or adding a new key at head- MRU |
| 15 | + node.prev = None |
| 16 | + node.next = self.head |
| 17 | + |
| 18 | + if self.head: |
| 19 | + self.head.prev = node |
| 20 | + self.head = node |
| 21 | + |
| 22 | + if self.tail is None: |
| 23 | + self.tail = node |
| 24 | + |
| 25 | + def remove(self, node): |
| 26 | + if node.prev: |
| 27 | + node.prev.next = node.next #previous node connects to the next node. |
| 28 | + else: |
| 29 | + self.head = node.next |
| 30 | + |
| 31 | + if node.next: |
| 32 | + node.next.prev = node.prev |
| 33 | + else: |
| 34 | + self.tail = node.prev |
| 35 | + |
| 36 | + node.prev = None |
| 37 | + node.next = None |
| 38 | + |
| 39 | + def pop_tail(self): #removes to return lru node |
| 40 | + if self.tail is None: |
| 41 | + return None |
| 42 | + node = self.tail |
| 43 | + self.remove(node) |
| 44 | + return node |
| 45 | + |
| 46 | + |
| 47 | + |
1 | 48 | class LruCache: |
2 | 49 | def __init__(self,limit) -> None: |
3 | 50 | if limit <= 0: |
4 | 51 | raise ValueError("Limit must be greater than zero") |
5 | 52 | self.limit = limit #defines a limit, after which before adding anything in our cache we need to remove it |
6 | | - self.storage = {} #stores data as key value pairs for fast lookups. accessing or updating a key both make it a Most recently used item |
7 | | - self.order = [] #lRU at index 0, MRU at last index position, tracks usage order |
| 53 | + self.storage = {} #key(cachekey) and node(key value pair) |
| 54 | + self.order = DoublyLinkedList() #self.head is MRU and self.tail is LRU |
8 | 55 | pass |
9 | 56 |
|
10 | 57 | #If a key already exists move it to MRU position |
11 | | - def touch (self,key): |
12 | | - if key in self.order: |
13 | | - self.order.remove(key) |
14 | | - self.order.append(key) |
| 58 | + def touch (self,node): |
| 59 | + self.order.remove(node) |
| 60 | + self.order.push_head(node) |
15 | 61 |
|
16 | 62 | # If we want to add or update a key value pair |
17 | 63 | def set(self, key, value): |
18 | 64 | if key in self.storage: |
19 | | - self.storage[key] = value #update the value of the key |
20 | | - self.touch(key) |
| 65 | + node = self.storage[key] #update the value of the key |
| 66 | + node.value = value |
| 67 | + self.touch(node) |
21 | 68 | return |
22 | 69 |
|
23 | | -#if we are adding a new key and at capacity |
| 70 | +#if we are adding a new key and at our limit |
24 | 71 | if len(self.storage) >= self.limit: |
25 | | - lru_key =self.order.pop(0) |
26 | | - del self.storage[lru_key] |
| 72 | + lru_node =self.order.pop_tail() |
| 73 | + del self.storage[lru_node.key] |
27 | 74 |
|
28 | | -#Add a new key and set it as MRU |
29 | | - self.storage[key]= value |
30 | | - self.touch(key) |
| 75 | +#insert a new node |
| 76 | + new_node = Node(key, value) |
| 77 | + self.order.push_head(new_node) |
| 78 | + self.storage[key] = new_node |
| 79 | + |
31 | 80 |
|
32 | 81 | def get(self,key): |
33 | 82 | if key not in self.storage: |
34 | 83 | return None |
35 | | - #if it exist set it as used |
36 | | - self.touch(key) |
37 | | - return self.storage[key] |
| 84 | + node = self.storage[key] |
| 85 | + self.touch(node) |
| 86 | + return node.value |
38 | 87 |
|
39 | | - |
40 | | -cache = LruCache(2) |
41 | | -cache.set("a", 1) # store = {a:1}, order = [a] |
42 | | -cache.set("b", 2) # store = {a:1,b:2}, order = [a,b] |
43 | | -print(cache.get("a")) # should print 1 |
44 | | -cache.get("a") # returns 1, order = [b,a] (a is now MRU) |
45 | | -cache.set("c", 3) # capacity reached, evict LRU = b |
46 | | -cache.get("b") |
47 | | -print(cache.order) # store = {a:1,c:3}, order = [a,c] |
|
0 commit comments