Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 179 additions & 0 deletions linked_list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
# Create a Node class to create a node
class Node:
def __init__(self, data):
self.data = data
self.next = None

# Create a LinkedList class
class LinkedList:
def __init__(self):
self.head = None

# Method to add a node at the beginning of the LL
def insertAtBegin(self, data):
new_node = Node(data)
new_node.next = self.head
self.head = new_node

# Method to add a node at any index
# Indexing starts from 0.
def insertAtIndex(self, data, index):
if index == 0:
self.insertAtBegin(data)
return

position = 0
current_node = self.head
while current_node is not None and position + 1 != index:
position += 1
current_node = current_node.next

if current_node is not None:
new_node = Node(data)
new_node.next = current_node.next
current_node.next = new_node
else:
print("Index not present")
Comment on lines +20 to +36

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This method should raise an IndexError for invalid indices (out of bounds or negative) instead of printing to the console. This is more idiomatic for Python data structures and allows callers to handle errors gracefully.

Suggested change
def insertAtIndex(self, data, index):
if index == 0:
self.insertAtBegin(data)
return
position = 0
current_node = self.head
while current_node is not None and position + 1 != index:
position += 1
current_node = current_node.next
if current_node is not None:
new_node = Node(data)
new_node.next = current_node.next
current_node.next = new_node
else:
print("Index not present")
def insertAtIndex(self, data, index):
if index < 0:
raise IndexError("Index cannot be negative")
if index == 0:
self.insertAtBegin(data)
return
position = 0
current_node = self.head
while current_node is not None and position + 1 != index:
position += 1
current_node = current_node.next
if current_node is not None:
new_node = Node(data)
new_node.next = current_node.next
current_node.next = new_node
else:
raise IndexError("Index out of bounds")


# Method to add a node at the end of LL
def insertAtEnd(self, data):
new_node = Node(data)
if self.head is None:
self.head = new_node
return

current_node = self.head
while current_node.next:
current_node = current_node.next

current_node.next = new_node

# Update node at a given position
def updateNode(self, val, index):
current_node = self.head
position = 0
while current_node is not None and position != index:
position += 1
current_node = current_node.next

if current_node is not None:
current_node.data = val
else:
print("Index not present")
Comment on lines +52 to +62

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This method should raise an IndexError for invalid indices (out of bounds or negative) instead of printing to the console. This allows callers to handle errors programmatically.

Suggested change
def updateNode(self, val, index):
current_node = self.head
position = 0
while current_node is not None and position != index:
position += 1
current_node = current_node.next
if current_node is not None:
current_node.data = val
else:
print("Index not present")
def updateNode(self, val, index):
if index < 0:
raise IndexError("Index cannot be negative")
current_node = self.head
position = 0
while current_node is not None and position != index:
position += 1
current_node = current_node.next
if current_node is not None:
current_node.data = val
else:
raise IndexError("Index out of bounds")


# Method to remove first node of linked list
def remove_first_node(self):
if self.head is None:
return

self.head = self.head.next

# Method to remove last node of linked list
def remove_last_node(self):
if self.head is None:
return

# If there's only one node
if self.head.next is None:
self.head = None
return

# Traverse to the second last node
current_node = self.head
while current_node.next and current_node.next.next:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The while loop condition can be simplified. The check for current_node.next is redundant. If current_node.next.next can be accessed without an error, it implies that current_node.next is not None.

Suggested change
while current_node.next and current_node.next.next:
while current_node.next.next:

current_node = current_node.next

current_node.next = None

# Method to remove a node at a given index
def remove_at_index(self, index):
if self.head is None:
return

if index == 0:
self.remove_first_node()
return

current_node = self.head
position = 0
while current_node is not None and current_node.next is not None and position + 1 != index:
position += 1
current_node = current_node.next

if current_node is not None and current_node.next is not None:
current_node.next = current_node.next.next
else:
print("Index not present")
Comment on lines +89 to +106

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This method should raise an IndexError for invalid indices (out of bounds or negative) instead of printing to the console or silently returning. This makes the behavior explicit and allows callers to handle errors.

    def remove_at_index(self, index):
        if index < 0:
            raise IndexError("Index cannot be negative")

        if self.head is None:
            raise IndexError("Remove from an empty list")

        if index == 0:
            self.remove_first_node()
            return

        current_node = self.head
        position = 0
        # Find predecessor of the node to be deleted
        while current_node.next is not None and position + 1 != index:
            position += 1
            current_node = current_node.next

        if current_node.next is not None:
            current_node.next = current_node.next.next
        else:
            raise IndexError("Index out of bounds")


# Method to remove a node from the linked list by its data
def remove_node(self, data):
current_node = self.head

# If the node to be removed is the head node
if current_node is not None and current_node.data == data:
self.remove_first_node()
return

# Traverse and find the node with the matching data
while current_node is not None and current_node.next is not None:
if current_node.next.data == data:
current_node.next = current_node.next.next
return
current_node = current_node.next

# If the data was not found
print("Node with the given data not found")
Comment on lines +109 to +125

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This method removes the first node that matches the given data. The name and comment could be more specific about this behavior. Also, instead of printing a message when the data is not found, it would be more idiomatic to return a boolean indicating whether the removal was successful. This makes the method more versatile.

Suggested change
def remove_node(self, data):
current_node = self.head
# If the node to be removed is the head node
if current_node is not None and current_node.data == data:
self.remove_first_node()
return
# Traverse and find the node with the matching data
while current_node is not None and current_node.next is not None:
if current_node.next.data == data:
current_node.next = current_node.next.next
return
current_node = current_node.next
# If the data was not found
print("Node with the given data not found")
# Method to remove the first node from the linked list by its data
def remove_node(self, data):
current_node = self.head
# If the node to be removed is the head node
if current_node is not None and current_node.data == data:
self.remove_first_node()
return True
# Traverse and find the node with the matching data
while current_node is not None and current_node.next is not None:
if current_node.next.data == data:
current_node.next = current_node.next.next
return True
current_node = current_node.next
# If the data was not found
return False


# Print the size of the linked list
def sizeOfLL(self):
size = 0
current_node = self.head
while current_node:
size += 1
current_node = current_node.next
return size
Comment on lines +128 to +134

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Instead of a custom method sizeOfLL, it's more Pythonic to implement the __len__ dunder method. This allows you to get the size of the list using the built-in len() function (e.g., len(llist)), which is more idiomatic and expected for collection-like objects in Python.

Suggested change
def sizeOfLL(self):
size = 0
current_node = self.head
while current_node:
size += 1
current_node = current_node.next
return size
def __len__(self):
size = 0
current_node = self.head
while current_node:
size += 1
current_node = current_node.next
return size


# Print the linked list
def printLL(self):
current_node = self.head
while current_node:
print(current_node.data)
current_node = current_node.next
Comment on lines +137 to +141

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The printLL method directly prints to the console. A more flexible and Pythonic approach is to implement the __str__ dunder method. This method should return a string representation of the list, which can then be printed or used in other ways (e.g., print(llist)).

Suggested change
def printLL(self):
current_node = self.head
while current_node:
print(current_node.data)
current_node = current_node.next
def __str__(self):
nodes = []
current_node = self.head
while current_node:
nodes.append(str(current_node.data))
current_node = current_node.next
return " -> ".join(nodes)



# create a new linked list
llist = LinkedList()

# add nodes to the linked list
llist.insertAtEnd('a')
llist.insertAtEnd('b')
llist.insertAtBegin('c')
llist.insertAtEnd('d')
llist.insertAtIndex('g', 2)

# print the linked list
print("Node Data:")
llist.printLL()

# remove nodes from the linked list
print("\nRemove First Node:")
llist.remove_first_node()
llist.printLL()

print("\nRemove Last Node:")
llist.remove_last_node()
llist.printLL()

print("\nRemove Node at Index 1:")
llist.remove_at_index(1)
llist.printLL()

# print the linked list after all removals
print("\nLinked list after removing a node:")
llist.printLL()

print("\nUpdate node Value at Index 0:")
llist.updateNode('z', 0)
llist.printLL()

print("\nSize of linked list:", llist.sizeOfLL())
Comment on lines +144 to +179
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Move example usage behind main guard for better modularity.

The example code demonstrates the functionality well, but executing at module level prevents clean imports. Consider wrapping in a main guard.

-# create a new linked list
-llist = LinkedList()
-
-# add nodes to the linked list
-llist.insertAtEnd('a')
-llist.insertAtEnd('b')
-llist.insertAtBegin('c')
-llist.insertAtEnd('d')
-llist.insertAtIndex('g', 2)
-
-# print the linked list
-print("Node Data:")
-llist.printLL()
-
-# remove nodes from the linked list
-print("\nRemove First Node:")
-llist.remove_first_node()
-llist.printLL()
-
-print("\nRemove Last Node:")
-llist.remove_last_node()
-llist.printLL()
-
-print("\nRemove Node at Index 1:")
-llist.remove_at_index(1)
-llist.printLL()
-
-# print the linked list after all removals
-print("\nLinked list after removing a node:")
-llist.printLL()
-
-print("\nUpdate node Value at Index 0:")
-llist.updateNode('z', 0)
-llist.printLL()
-
-print("\nSize of linked list:", llist.sizeOfLL())
+if __name__ == "__main__":
+    # create a new linked list
+    llist = LinkedList()
+    
+    # add nodes to the linked list
+    llist.insertAtEnd('a')
+    llist.insertAtEnd('b')
+    llist.insertAtBegin('c')
+    llist.insertAtEnd('d')
+    llist.insertAtIndex('g', 2)
+    
+    # print the linked list
+    print("Node Data:")
+    llist.printLL()
+    
+    # remove nodes from the linked list
+    print("\nRemove First Node:")
+    llist.remove_first_node()
+    llist.printLL()
+    
+    print("\nRemove Last Node:")
+    llist.remove_last_node()
+    llist.printLL()
+    
+    print("\nRemove Node at Index 1:")
+    llist.remove_at_index(1)
+    llist.printLL()
+    
+    # print the linked list after all removals
+    print("\nLinked list after removing a node:")
+    llist.printLL()
+    
+    print("\nUpdate node Value at Index 0:")
+    llist.updateNode('z', 0)
+    llist.printLL()
+    
+    print("\nSize of linked list:", llist.sizeOfLL())
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# create a new linked list
llist = LinkedList()
# add nodes to the linked list
llist.insertAtEnd('a')
llist.insertAtEnd('b')
llist.insertAtBegin('c')
llist.insertAtEnd('d')
llist.insertAtIndex('g', 2)
# print the linked list
print("Node Data:")
llist.printLL()
# remove nodes from the linked list
print("\nRemove First Node:")
llist.remove_first_node()
llist.printLL()
print("\nRemove Last Node:")
llist.remove_last_node()
llist.printLL()
print("\nRemove Node at Index 1:")
llist.remove_at_index(1)
llist.printLL()
# print the linked list after all removals
print("\nLinked list after removing a node:")
llist.printLL()
print("\nUpdate node Value at Index 0:")
llist.updateNode('z', 0)
llist.printLL()
print("\nSize of linked list:", llist.sizeOfLL())
if __name__ == "__main__":
# create a new linked list
llist = LinkedList()
# add nodes to the linked list
llist.insertAtEnd('a')
llist.insertAtEnd('b')
llist.insertAtBegin('c')
llist.insertAtEnd('d')
llist.insertAtIndex('g', 2)
# print the linked list
print("Node Data:")
llist.printLL()
# remove nodes from the linked list
print("\nRemove First Node:")
llist.remove_first_node()
llist.printLL()
print("\nRemove Last Node:")
llist.remove_last_node()
llist.printLL()
print("\nRemove Node at Index 1:")
llist.remove_at_index(1)
llist.printLL()
# print the linked list after all removals
print("\nLinked list after removing a node:")
llist.printLL()
print("\nUpdate node Value at Index 0:")
llist.updateNode('z', 0)
llist.printLL()
print("\nSize of linked list:", llist.sizeOfLL())
🤖 Prompt for AI Agents
In linked_list.py around lines 144 to 179, the example usage code runs at the
module level, which can cause unwanted execution when importing this file. To
fix this, wrap all the example usage code inside a main guard by placing it
under if __name__ == "__main__":. This ensures the code only runs when the
script is executed directly, improving modularity and import safety.

Comment on lines +144 to +179

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The script includes test and demonstration code at the module level. This code will execute whenever the file is imported as a module into another script, which is usually undesirable. To make this file reusable as a module, you should place this code inside an if __name__ == "__main__": block.

if __name__ == "__main__":
    # create a new linked list
    llist = LinkedList()

    # add nodes to the linked list
    llist.insertAtEnd('a')
    llist.insertAtEnd('b')
    llist.insertAtBegin('c')
    llist.insertAtEnd('d')
    llist.insertAtIndex('g', 2)

    # print the linked list
    print("Node Data:")
    llist.printLL()

    # remove nodes from the linked list
    print("\nRemove First Node:")
    llist.remove_first_node()
    llist.printLL()

    print("\nRemove Last Node:")
    llist.remove_last_node()
    llist.printLL()

    print("\nRemove Node at Index 1:")
    llist.remove_at_index(1)
    llist.printLL()

    # print the linked list after all removals
    print("\nLinked list after removing a node:")
    llist.printLL()

    print("\nUpdate node Value at Index 0:")
    llist.updateNode('z', 0)
    llist.printLL()

    print("\nSize of linked list:", llist.sizeOfLL())