West Midlands | 26 March SDC | Iswat Bello | Sprint 2 | Implement a linked list in python#191
West Midlands | 26 March SDC | Iswat Bello | Sprint 2 | Implement a linked list in python#191Iswanna wants to merge 12 commits into
Conversation
- Define Node class to store value - Add pointers for next and previous nodes
- Define the LinkedList class in linked_list.py. - Implement the __init__ method to track the head and tail of the list. - Initialize head and tail to None for an empty list state.
- Added the logic to create a new node and insert it at the front of the list. - Handled pointer updates for both empty and non-empty list states. - Returns the new node to allow for future removal/referencing.
- Added logic to remove the last node from the list. - Handles edge cases for empty lists and single-node lists. - Updates tail pointers and returns the value of the removed node.
- Added logic to remove a specific node from any position. - Included pointer updates for head and tail edge cases. - Added neighbor re-linking to maintain list integrity.
- Added test_remove_middle to verify pointer re-linking when deleting from the center. - Added test_pop_empty_list to ensure the program doesn't crash on empty lists. - Added test_remove_none to verify handling of null input.
- Documented core logic for Node and LinkedList classes - Explained testing strategy for edge cases (empty lists, middle removal) - Traded extra memory space to achieve O(1) time (constant speed).
- Changed "Linked List" to "Doubly Linked List" to accurately reflect the bi-directional implementation. - Updated header for better technical clarity.
This comment has been minimized.
This comment has been minimized.
1 similar comment
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
1 similar comment
This comment has been minimized.
This comment has been minimized.
| """ | ||
| Represents a single person in line. | ||
| Stores a value and links to the people ahead and behind. | ||
| """ | ||
| def __init__(self, value): | ||
| self.value = value | ||
| self.next = None # The person ahead | ||
| self.previous = None # The person behind |
There was a problem hiding this comment.
May I suggest exploring the use of __slots__ to reduce memory usage?
There was a problem hiding this comment.
Thank you for the suggestion. I have implemented __slots__ to reduce memory usage.
| # Logic 3: If the list is not empty | ||
| else: | ||
| new_node.next = self.head # New person grabs old head's hand | ||
| self.head.previous = new_node # Old head reaches back to new person | ||
| self.head = new_node # New person is now the head |
There was a problem hiding this comment.
-
elsealready means "opposite", so you could probably drop the comment on line 28. -
The comments on line 30-32 actually confuses me because it refers to "person". Are they your own notes? If so, it might not be a good idea to leave them in "production" code.
There was a problem hiding this comment.
Thank you for the feedback.
Those metaphors were part of my initial notes to help me understand the concept. I've cleaned up the code to remove the non-technical language and the redundant logic labels.
|
|
||
| def push_head(self, value): |
There was a problem hiding this comment.
Could consider adding type hints to each method to clearly specify the types of its parameters and return value.
There was a problem hiding this comment.
Thank you for the feedback. I have updated the code to implement your suggestions.
| # Logic 2: If there is only one person in line | ||
| if self.head == self.tail: | ||
| self.head = None | ||
| self.tail = None | ||
| # Logic 3: If there are more people | ||
| else: | ||
| self.tail = self.tail.previous # Move tail back one | ||
| self.tail.next = None # New tail lets go of the old tail |
There was a problem hiding this comment.
Thank you for the suggestion. I’ve refactored the pop_tail method to call self.remove(self.tail)
| # Logic 1: Store the value to return later | ||
| value_to_return = self.tail.value |
There was a problem hiding this comment.
The variable name is self-explanatory. The comment does not provide any more info.
There was a problem hiding this comment.
I have removed all redundant comments. Thank you.
| # Logic 2: If there is only one person in line | ||
| if self.head == self.tail: | ||
| self.head = None | ||
| self.tail = None | ||
| # Logic 3: If there are more people |
There was a problem hiding this comment.
What do these "logic numbers" represent?
There was a problem hiding this comment.
They represented the steps of the logic, but I have now removed them.
- Add __slots__ to prevent per-instance dictionary overhead - Optimize space complexity for large-scale list usage
- Remove numbered logic comments (# Logic 1, 2, 3) to reduce noise - Remove non-technical metaphors (e.g., "New person") in favor of self-documenting code - Clean up method bodies to follow professional clean code standards
- Implement typing (Any, Optional) for parameters and return values - Add forward references for Node pointers
- Replace manual pointer logic in pop_tail with a call to self.remove() - Adhere to DRY (Don't Repeat Yourself) principles - Reduce code duplication and improve maintainability
cjyuan
left a comment
There was a problem hiding this comment.
Changes look great. Code is very clean!
If you want to enforce that a linked list can only contain values of a single type (e.g. all ints or all strs), have a look at Python's TypeVar and Generic type hints. They're commonly used to make data structures type-safe while remaining reusable.
Learners, PR Template
Self checklist
Changelist
In this PR, I implemented a Doubly Linked List to handle data storage and removal efficiently. A detailed breakdown of the implementation and trade-offs can be found in the
CHANGES-MADE.mdfile included in this submission.Key Changes
Nodeclass with bidirectional pointers (nextandprevious) and aLinkedListclass to manage theheadandtail.push_head(value): Adds a node to the front in O(1) time.pop_tail(): Removes the last node in O(1) time, including logic to handle single-node and empty lists.remove(node): A robust method that re-links neighboring nodes to remove a specific entry from anywhere in the list.head,tail,next, andprevious) are correctly updated during every operation to maintain list integrity.Testing Done
I verified the code using
unittestwith the following scenarios:push_headandpop_tailsequences.pop_tailreturnsNonerather than crashing when the list is empty.remove(None)is handled gracefully.Learning Reflection (Trade-offs)
I applied a space-vs-time trade-off by using extra memory (for the
previouspointer) to ensure that adding to the head and removing from the tail stays at O(1) speed regardless of how many items are in the list.