-
-
Notifications
You must be signed in to change notification settings - Fork 29
Sheffield | 25-SDC-Nov | Sheida Shabankari | Sprint 2 | Implement LRU cache in python #105
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 6 commits
abfb1fe
257be0e
f30e7c6
eff57e4
b64d584
3ee5de2
d53054c
3fd9e83
6132cf2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,150 @@ | ||
| class Node: | ||
| """ | ||
| Represents a node in a doubly linked list. | ||
| Each node holds a key-value pair and pointers to the previous and next nodes. | ||
| """ | ||
| def __init__(self,key,value): | ||
| self.key=key | ||
| self.value=value | ||
| self.next=None | ||
| self.previous=None | ||
|
|
||
| class LinkedList: | ||
| """ | ||
| A doubly linked list supporting: | ||
| - push_head: add a node to the head (most recently used position) | ||
| - pop_tail: remove and return the tail node (least recently used) | ||
| - remove: remove a specific node from the list | ||
| Maintains references to both head and tail nodes for O(1) operations. | ||
| """ | ||
| def __init__(self): | ||
| self.head=None | ||
| self.tail=None | ||
|
|
||
| def push_head(self,node=None,key=None,value=None) -> Node: | ||
| """ | ||
| Adds a node to the head of the list. | ||
| If 'node' is provided, it is moved to the head. | ||
| If 'node' is None, a new node is created from key and value. | ||
| """ | ||
| if node is None: | ||
| new_node=Node(key,value) | ||
| else: | ||
| self.remove(node) | ||
| new_node=node | ||
|
|
||
| if self.head is None: | ||
| self.head=new_node | ||
| self.tail=new_node | ||
| else: | ||
| old_head=self.head | ||
| self.head=new_node | ||
| new_node.next=old_head | ||
| old_head.previous=new_node | ||
| new_node.previous=None | ||
| return new_node | ||
|
|
||
| def pop_tail(self) -> any: | ||
| """ | ||
| Removes the tail node (least recently used) from the list | ||
| and returns its key and value as a tuple. | ||
| """ | ||
| if self.head is None: | ||
| return None | ||
| elif self.head==self.tail: | ||
| node_value=self.head.value | ||
| node_key=self.head.key | ||
| self.head=None | ||
| self.tail=None | ||
| else: | ||
| node_value=self.tail.value | ||
| node_key=self.tail.key | ||
| previous_node=self.tail.previous | ||
| self.tail=previous_node | ||
| self.tail.next=None | ||
| return (node_key,node_value) | ||
|
|
||
| def remove(self,node) -> None: | ||
| """ | ||
| Removes a specific node from the list. | ||
| Handles nodes at the head, tail, or middle of the list. | ||
| """ | ||
| node_to_remove=node | ||
| current_head=self.head | ||
| current_tail=self.tail | ||
| if current_head==current_tail and node_to_remove==current_head: | ||
| #list has only one node | ||
| self.head=None | ||
| self.tail=None | ||
| elif node_to_remove.next is None: | ||
| #Node is the tail | ||
| self.tail=node_to_remove.previous | ||
| self.tail.next=None | ||
| elif node_to_remove.previous is None: | ||
| #Node is the head | ||
| self.head=node_to_remove.next | ||
| self.head.previous=None | ||
| else: | ||
| #Node is in the middle of list | ||
| previous_node=node_to_remove.previous | ||
| next_node=node_to_remove.next | ||
| previous_node.next=next_node | ||
| next_node.previous=previous_node | ||
|
|
||
| node_to_remove.next=None | ||
| node_to_remove.previous=None | ||
|
|
||
| class LruCache: | ||
| def __init__(self,limit): | ||
| """ | ||
| Initialize the LRU Cache. | ||
| Args: limit (int): Maximum number of items the cache can hold. | ||
| """ | ||
| if limit<=0: | ||
| raise ValueError("limit must be greater than zero") | ||
| self.limit=limit | ||
| self.map={} | ||
| self.List=LinkedList() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why start the name the property
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wasn’t paying attention to Python’s naming conventions for properties. I realized that property names should be lowercase, so I’ve renamed List to list to follow the standard convention. |
||
| self.count=0 | ||
|
|
||
| def get(self,key) ->any: | ||
| """ | ||
| Retrieve value by key and mark node as most recently used. | ||
| Args: key: Key to look up in cache. | ||
| Returns: The value associated with the key, or None if not found. | ||
| """ | ||
| if key not in self.map: | ||
| return None | ||
| node=self.map[key] | ||
| self.List.push_head(node=node) | ||
| return node.value | ||
|
|
||
|
|
||
| def set(self,key,value): | ||
| """ | ||
| Add a new key-value pair or update an existing key. | ||
| Args: | ||
| key: Key to insert or update. | ||
| value: Value to associate with the key. | ||
| Behavior: | ||
| - Updates value if key exists and moves node to head. | ||
| - Inserts new node at head if key does not exist. | ||
| - Evicts least recently used node if cache exceeds limit. | ||
| """ | ||
| if key in self.map: | ||
| node=self.map[key] | ||
| node.value=value | ||
| self.List.push_head(node=node) | ||
| else: | ||
| if self.count>=self.limit: | ||
| old_key,old_value=self.List.pop_tail() | ||
| del self.map[old_key] | ||
| self.count -=1 | ||
|
|
||
| new_node=self.List.push_head(key=key,value=value) | ||
| self.count +=1 | ||
| self.map[key]=new_node | ||
|
|
||
|
Comment on lines
+41
to
+55
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is possible to use the The drawback is, we can't reuse the node when we want to move it to the front. if key in self.map:
node=self.map[key]
self.list.remove(node)
self.map[key] = self.list.push_head((key, value))
else:
if self.count>=self.limit:
old_key, _ = self.list.pop_tail()
del self.map[old_key]
self.count -=1
self.map[key] = self.list.push_head((key, value))
self.count +=1
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for your useful feedbacks. |
||
|
|
||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moving a node to the front does not seem like a normal behavior expected from the "push head" operation.
You could consider implementing a helper function or just have the caller explicitly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did it.