π‘ Problem Formulation: When working with circular linked lists in Python, a common issue arises when the list contains duplicate values. This can cause erroneous data processing and storage inefficiencies. Our goal is to implement a Python program that traverses the circular linked list and removes any duplicate nodes, ensuring each value is unique. For example, given a circular linked list with elements 3 -> 5 -> 3 -> 2 -> 5 we want it to become 3 -> 5 -> 2.
Method 1: Hashing
This method involves the use of hashing to track the occurrences of the elements in the circular linked list. We traverse the list, store each element inside a hash table, and skip over or delete nodes that have already been encountered, thereby removing duplicates efficiently.
Here’s an example:
class Node:
def __init__(self, data):
self.data = data
self.next = None
def remove_duplicates(head):
current = head
prev = None
nodes_seen = set()
while True:
if current.data in nodes_seen:
prev.next = current.next
else:
nodes_seen.add(current.data)
prev = current
current = current.next
if current == head:
break
# Example setup and call
head = Node(3)
head.next = Node(5)
head.next.next = Node(3)
head.next.next.next = Node(2)
head.next.next.next.next = Node(5)
head.next.next.next.next.next = head
remove_duplicates(head)Output: 3 -> 5 -> 2
This code snippet defines a Node class and a remove_duplicates function that takes the head of a circular linked list and removes duplicate elements. It uses a Python set to keep track of seen nodes while iterating over the list, and it alters the next pointer of previous nodes to remove duplicates.
Method 2: Two Pointer Approach
In this method, two pointers are used to traverse the circular linked list. The ‘current’ pointer goes through each element, while the ‘runner’ pointer checks for duplicates ahead of ‘current’. This method removes duplicates without using extra space but has a higher time complexity of O(n^2).
Here’s an example:
def remove_duplicates(head):
current = head
while current.next != head:
runner = current
while runner.next != head:
if runner.next.data == current.data:
runner.next = runner.next.next
else:
runner = runner.next
current = current.next
# Example setup and call with the same previous setup for the circular linked list.Output: 3 -> 5 -> 2
This method iterates the list with a current pointer and uses another runner pointer to scan ahead for any duplicates of the current node’s data, deleting them by adjusting pointers.
Method 3: Sorting and Removing Duplicates
This method starts by sorting the circular linked list, then traverses the sorted list to remove duplicates, which are now adjacent. Though this method modifies the original list order, it guarantees the removal of duplicates.
Here’s an example: (Please note that sorting a circular linked list will require breaking the list, sorting it with any standard method, and then relinking. This pseudo-example will not include sorting code.)
def remove_duplicates_sorted(head):
current = head
while current.next != head:
if current.data == current.next.data:
current.next = current.next.next
else:
current = current.next
# Example setup and call with a sorted circular linked list assumed.Output: 2 -> 3 -> 5
Assuming the circular linked list is sorted, the remove_duplicates_sorted function traverses the list and removes duplicates in a straightforward manner by skipping over nodes with the same data.
Method 4: Recursive Approach
This method applies a recursive strategy to remove duplicates. Starting from the head of the list, it recursively visits each node and checks for duplicates. The method requires careful handling to avoid infinite loops due to the circular nature of the list.
Here’s an example:
def remove_duplicates_recursive(node, head, prev=None):
if not node or node.next == head:
return
if prev and prev.data == node.data:
prev.next = node.next
remove_duplicates_recursive(node.next, head, prev)
else:
remove_duplicates_recursive(node.next, head, node)
# Example setup and call with the same initial circular linked list setup.Output: 3 -> 5 -> 2
The remove_duplicates_recursive function traverses the circular linked list and removes duplicates by adjusting the ‘next’ pointers of previous nodes if a duplicate is found. The base case for the recursion is reaching back at the head node.
Bonus One-Liner Method 5: Using Python Collections.deque
This method is a fun and less conventional way to remove duplicates using the Python’s collections.deque. It involves converting the circular linked list to a deque, removing duplicates and reconstructing the circular linked list. Note: This will disrupt the original structure and ordering.
Here’s an example:
from collections import deque
def remove_duplicates_deque(head):
dq = deque()
current = head.next
dq.append(head.data)
while current != head:
if current.data not in dq:
dq.append(current.data)
current = current.next
# Reconstruction of the circular linked list from dq is needed here.
# Example setup and call with the initial circular linked list setup.Output: 3 -> 5 -> 2
The remove_duplicates_deque function creates a deque from the circular linked list, ensuring each element is unique. It then requires a re-construction of the circular linked list from the deque.
Summary/Discussion
- Method 1: Hashing. Fast and efficient. Requires extra space for the hash table. Best for larger lists where time complexity is a priority.
- Method 2: Two Pointer Approach. No extra space used. Slower for large lists due to O(n^2) time complexity. Better if memory usage is a concern.
- Method 3: Sorting and Removing Duplicates. Changes the original list order. Requires breaking and re-linking the circular property of the list. Efficient if presorting is acceptable.
- Method 4: Recursive Approach. Elegant and concise. Can be less intuitive. Stack depth can be an issue for large lists.
- Method 5: Using Python Collections.deque. Quick for smaller lists. Requires complete reconstruction of the list. Not suitable for very large lists due to potential performance concerns.
