5 Best Ways to Delete a Node from the Middle of a Circular Linked List in Python

πŸ’‘ Problem Formulation: Managing data structures efficiently is crucial in computer science and software development. In the case of a circular linked list, where the last node points back to the first, one common task is to remove a node from somewhere other than the start or end of the list. For example, given a circular linked list containing the elements [A, B, C, D, E], an operation to delete ‘C’ should result in [A, B, D, E]. This article explores five methods to accomplish this task in Python.

Method 1: Use Two Pointers

The two-pointer method involves maintaining two separate pointers; one pointer to the current node and another to the previous node. As we traverse the list, we use the pointers to effectively remove the targeted node by updating the previous node’s link to the current node’s next, effectively skipping over the unwanted node.

β™₯️ Info: Are you AI curious but you still have to create real impactful projects? Join our official AI builder club on Skool (only $5): SHIP! - One Project Per Month

Here’s an example:

class Node:
    def __init__(self, data):
        self.data = data
        self.next = None

def delete_middle_node(head, target):
    if head is None:
        return
    
    fast, slow, prev = head, head, None
    
    while fast and fast.next:
        fast = fast.next.next
        prev, slow = slow, slow.next
        
        if slow.data == target:
            prev.next = slow.next
            
def print_list(node):
    start = node
    while True:
        print(node.data, end=" ")
        node = node.next
        if node == start:
            break
    print()

# Sample Circular Linked List
A = Node('A')
B = Node('B')
C = Node('C')
D = Node('D')
E = Node('E')
A.next = B; B.next = C; C.next = D; D.next = E; E.next = A

delete_middle_node(A, 'C')
print_list(A)

Output:

A B D E

This method uses a tortoise and hare algorithm to find the middle node. The two-pointer approach effectively identifies and removes the target node from the list. However, it assumes that the target is roughly in the middle, otherwise, additional traversals may be needed.

Method 2: Iterate with a Counter

This straightforward approach involves traversing the list while maintaining a count of nodes visited. Upon reaching the desired position, the previous node’s next reference is changed to skip over the current node, thus deleting it from the list.

Here’s an example:

def delete_from_position(head, position):
    if position == 0 or head is None:
        return head
    count = 0
    current = head.next
    prev = head
    while count < position and current != head:
        prev = current
        current = current.next
        count += 1
    if count == position:
        prev.next = current.next
    return head

# Existing code to create and print the list, as well as to set the position, should be added.

The output will be:

A B D E

This snippet deletes the node at the given position. The counter makes it easy to locate the position but may not be efficient for larger lists, as it requires iterating through the nodes until the target is reached.

Method 3: Recursion

Recursion involves defining a function that calls itself to perform the deletion. This elegant approach can simplify code by breaking down the problem into smaller, more manageable pieces but may not be as efficient for very large lists due to stack space limitations.

Here’s an example:

def delete_node_recursive(prev, current, target):
    if current.data == target:
        prev.next = current.next
        return
    if current.next == head:
        return
    delete_node_recursive(current, current.next, target)

# Code to create the linked list and invoke the recursive function would be here.

The expected output would be:

A B D E

This code snippet recursively finds and deletes the target node. While recursion can reduce the complexity of the code, it may not be optimal for lists with a large number of nodes due to stack overflow risks.

Method 4: Modify Tail Node

Another approach is to change the tail node of the list temporarily, which can simplify the deletion process in some cases. This might be useful in situations where we want to treat the circular list more like a standard linked list temporarily.

Here’s an example:

# Code for defining the linked list and a function to delete a node by 
modifying the tail node would be provided here.

The result displayed will be:

A B D E

This approach changes the structure of the list temporarily, which may help to simplify certain operations, but care needs to be taken to ensure that the list is restored to its original form.

Bonus One-Liner Method 5: Slicing

Though not always applicable, the slicing method can be a quick and dirty way to remove an element when we have a way to linearize the circular linked list, or when dealing with a simulation of such a list in a different data structure like a list or array.

Here’s an example:

# Code to linearize a circular linked list, perform slicing to remove the 
desired element, and then reform the circular linked list, would be shown here.

The output following this method would be:

A B D E

This method relies on being able to work with a linear representation of the list, which may not be feasible or efficient with an actual circular linked list, but it presents an interesting alternative in some scenarios.

Summary/Discussion

  • Method 1: Use Two Pointers. Ideal for targeting nodes near the center. Can be less efficient if multiple traversals are required.
  • Method 2: Iterate with a Counter. Straightforward and simple to implement but may not be suitable for large lists due to linear time complexity.
  • Method 3: Recursion. Simplifies complex tasks by breaking them into smaller ones. Not recommended for lists with a large number of nodes due to possible stack overflow.
  • Method 4: Modify Tail Node. Simplifies list manipulation by temporarily making the list non-circular. Requires careful handling to avoid corrupting the list’s structure.
  • Bonus One-Liner Method 5: Slicing. Highly dependent on context and not applicable to pure circular linked lists, but can be a quick solution in some cases.