Question
Given a Linked List, how can you detect and remove a loop from that linked list efficiently?
Sample Test Cases:
Test Case 1
Sample Input
Expected Output 13-->24-->26-->38-->41-->NULL.
Test Case 2
Sample Input
Expected Output
55-->45-->54-->63-->56-->NULL
Test Case 3
Sample Input
Expected Output
7-->9-->8-->6-->NULL
Explanation
As the problem states, we need to find a loop in a LinkedList and remove it. If the loop was found and successfully removed, return true and print the list after removing the loop else, if the loop was not found, return false. For example, the linked list in sample test case 1, after removing the loop should change to 13-->24-->26-->38-->41-->NULL. Before removing the loop, it is important to detect it. To detect the loop we use Floyd’s Cycle detection algorithm. To remove the loop, the pointer to the last node of the loop is required which is node 41 in the above test case. After having the pointer to the last node of the loop, we just need to set its next pointer to null. After detecting the loop, there are different methods to approach this problem. We have discussed them below.
Naive Approach
- Floyd’s Cycle Detection Algorithm will stop when fast and slow pointers meet at a common point.
- This common point will be one of the nodes that are part of the loop.
- Store the address of this common point in a node variable named loopNode.
- Later, start from the head node of the Linked List and check whether any one of all nodes is reachable from loopNode one by one.
- Each time a reachable node will be found, that node will be the start node of the loop in the Linked List and the pointer to the node that is previous to this node can be found.
Java Code
import java.lang.*; import java.util.*; //LinkedListClass and Node class class List { static Node head; static class Node { int data; Node next; Node(int d) { data = d; next = null; } } //detect loop in the list int detectLoop(Node node) { Node slow = node, fast = node; while (slow != null && fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; // If slow and fast meet at the same point then a loop is there return 1; if (slow == fast) { removeLoop(slow, node); return 1; } } //else return 0 return 0; } // remove loop void removeLoop(Node loop, Node curr) { Node head = null, loopNode = null; /* move the pointer to the beginning of the Linked List one by one */ head = curr; while (true) { /* Now start a pointer from node where they meet and check if it ever reaches loopNode*/ loopNode = loop; while (loopNode.next != loop && loopNode.next != head) { loopNode = loopNode.next; } /* If loopNode reached head then there is a loop. So break it */ if (loopNode.next == head) { break; } // If loopNode didn't reach head then try the next node after head head = head.next; } /* After the end of the loop loopNode is the last node of the loop. So make next of loopNode as NULL */ loopNode.next = null; } // print the linked list void print(Node node) { while (node != null) { System.out.print(node.data + " "); node = node.next; } } // Driver code public static void main(String[] args) { List list = new List(); list.head = new Node(13); list.head.next = new Node(24); list.head.next.next = new Node(26); list.head.next.next.next = new Node(38); list.head.next.next.next.next = new Node(41); // testing by creating a loop head.next.next.next.next.next = head.next.next; list.detectLoop(head); System.out.println( "Linked List after removing loop : "); list.print(head); } }
Output Screenshot
Python Code
class Node: # Constructor for node object def __init__(self, data): self.data = data self.next = None class LinkedList: # initialize head def __init__(self): self.head = None def detectLoop(self): slow = fast = self.head while(slow and fast and fast.next): slow = slow.next fast = fast.next.next # If slow and fast meet # then there is a loop if slow == fast: self.removeLoop(slow) # Return 1 to indicate that loop if found return 1 # Return 0 to indicate that there is no loop return 0 # Function to remove loop # loop node= Pointer to loop node # head = Pointer to the start node of the linked list def removeLoop(self, loop_node): head = self.head while(1): # Now start a pointer from loop_node and check # if it ever reaches loopnode loopnode = loop_node while(loopnode.next != loop_node and loopnode.next != head): loopnode = loopnode.next # If loopnode reached head then there is a loop. # So break the loop if loopnode.next == head: break head = head.next # After the end of loop loopnode is the last node of # the loop. So make next of loopnode as NULL loopnode.next = None # insert a new node at the beginning def push(self, new_data): new_node = Node(new_data) new_node.next = self.head self.head = new_node # function to print the linked LinkedList def printL(self): temp = self.head while(temp): print (temp.data) temp = temp.next # Driver code llist = LinkedList() llist.push(13) llist.push(24) llist.push(26) llist.push(38) llist.push(41) # Create a loop for testing llist.head.next.next.next.next.next = llist.head.next.next llist.detectLoop() print ("after removing loop") llist.printL()
Output Screenshot
C++ Code
#include <bits/stdc++.h> using namespace std; /* list node */ struct Node { int data; struct Node* next; }; /* Function to remove loop. Used by detectLoop() */ void removeLoop(struct Node*, struct Node*); /* This function detects and removes loop in the list*/ int detectLoop(struct Node* list) { struct Node *slow = list, *fast = list; while (slow && fast && fast->next) { slow = slow->next; fast = fast->next->next; /* If slow and fast meet then there is a loop */ if (slow == fast) { removeLoop(slow, list); /* Return 1 : loop is found */ return 1; } } /* Return 0 :there is no loop*/ return 0; } /* Function to remove loop. loop_node : Points to one of the loop nodes head : Points to the start node of the list */ void removeLoop(struct Node* loop_node, struct Node* head) { struct Node* loopNode; struct Node* curr; /* Move the pointer to the beginning Linked List and move it one position at a time to get the first node */ loopNode = head; while (1) { /* Now start a pointer from loop_node and check if it ever reaches ptr2 */ curr = loop_node; while (curr->next != loop_node && curr->next != loopNode) curr = curr->next; /* If curr reached loopNode then a loop is there. So break it */ if (curr->next == loopNode) break; /* If curr didn't reach loopNode then try the next node * after loopNode */ loopNode = loopNode->next; } /* at the end, curr will be the last node of the loop. So make next of curr as NULL */ curr->next = NULL; } /* print linked list */ void printL(struct Node* node) { while (node != NULL) { cout << node->data << " "; node = node->next; } } struct Node* newNode(int key) { struct Node* temp = new Node(); temp->data = key; temp->next = NULL; return temp; } // Driver Code int main() { struct Node* head = newNode(13); head->next = newNode(24); head->next->next = newNode(26); head->next->next->next = newNode(38); head->next->next->next->next = newNode(41); head->next->next->next->next->next = head->next->next; detectLoop(head); cout << " List after removing loop" << endl; printL(head); return 0;
Output Screenshot
Time Complexity
The time complexity for this code is O(n^2) where n is the number of nodes in the Linked List. It is O(n^2) because, for every node of the Linked List, we have to traverse the entire Linked List to check if that node is reachable from the loop node.
Space Complexity
The space complexity of this code is O(1) because we are not using any additional space or data structure to store the values.
Efficient Method
- The first step of this method is the same, use Floyd’s Cycle Detection Algorithm to detect loop and get the pointer to a node belonging to the loop.
- Count the number of nodes in the loop and set the count to c.
- Set a pointer at the head and another at c’th node from the head.
- Move both the pointers at equal speed, they will meet at the starting node of the loop.
- Get the pointer to the last node of the loop and set its next pointer to null.
Java Code
class LinkedList { static Node head; static class Node { int data; Node next; Node(int d) { data = d; next = null; } } // detect loop in the list int detectLoop(Node node) { Node slow = node, fast = node; while (slow != null && fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; // If loop is present if (slow == fast) { removeLoop(slow, node); return 1; } } //if no loop return 0; } // Function to remove loop void removeLoop(Node loop, Node head) { Node loopNode = loop; Node curr = loop; // Count the number of nodes in loop int k = 1; while (loopNode.next != curr) { loopNode = loopNode.next; k++; } // Fix one pointer to head loopNode = head; // set other pointer to k nodes after head curr = head; for (int i = 0; i < k; i++) { curr = curr.next; } // Move both pointers at the same pace while (curr != loopNode) { loopNode = loopNode.next; curr = curr.next; } // Get pointer to the last node while (curr.next != loopNode) { curr = curr.next; } /* Set the next node of the loop ending node to null*/ curr.next = null; } // print the linked list void printL(Node node) { while (node != null) { System.out.print(node.data + " "); node = node.next; } } // Driver program public static void main(String[] args) { LinkedList list = new LinkedList(); list.head = new Node(55); list.head.next = new Node(45); list.head.next.next = new Node(54); list.head.next.next.next = new Node(63); list.head.next.next.next.next = new Node(56); // loop for testing head.next.next.next.next.next = head.next.next; list.detectLoop(head); System.out.println("Linked List after removing loop : "); list.printL(head); } }
Output Screenshot
Python Code
# Node class class Node: # Constructor def __init__(self, data): self.data = data self.next = None class LinkedList: # Function to initialize head def __init__(self): self.head = None def detectLoop(self): slow = fast = self.head while(slow and fast and fast.next): slow = slow.next fast = fast.next.next # If slow and fast meet then there is a loop if slow == fast: self.removeLoop(slow) # Return 1 if a loop is found return 1 # Return 0 if there is no loop return 0 # Function to remove loop # loop_node --> pointer to one of the loop nodes # head --> Pointer to the start node of the linked list def removeLoop(self, loop_node): loopnode= loop_node curr = loop_node # get number of nodes in loop k = 1 while(loopnode.next != curr): loopnode = loopnode.next k += 1 # set one pointer to head loopnode = self.head # other pointer to k nodes after head curr= self.head for i in range(k): curr = curr.next # Move both pointers at the same pace while(curr != loopnode): loopnode = loopnode.next curr = curr.next # Get pointer to the last node while(curr.next != loopnode): curr = curr.next # Set the next node of the loop ending node to null curr.next = None # insert a new node at the beginning def push(self, new_data): new_node = Node(new_data) new_node.next = self.head self.head = new_node # print the linked LinkedList def printL(self): temp = self.head while(temp): print (temp.data) temp = temp.next # Driver program llist = LinkedList() llist.push(55) llist.push(45) llist.push(54) llist.push(63) llist.push(41) # Create a loop for testing llist.head.next.next.next.next.next = llist.head.next.next llist.detectLoop() print ("after removing loop") llist.printL()
Output Screenshot
C++ Code
#include <bits/stdc++.h> using namespace std; /* Linked list node */ struct Node { int data; struct Node* next; }; /* remove loop. */ void removeLoop(struct Node*, struct Node*); /* This function detects and removes loop in the list If loop is present returns 1, otherwise returns 0 */ int detectLoop(struct Node* list) { struct Node *slow = list, *fast = list; // loop exists or not while (slow && fast && fast->next) { slow = slow->next; fast = fast->next->next; /* If slow and fast meet then there is a loop */ if (slow == fast) { removeLoop(slow, list); /* Return 1 if loop is found */ return 1; } } /* Return 0 if there is no loop*/ return 0; } /* Function to remove loop. loop_node : Pointer to one of the loop nodes head : Pointer to the start node of the linked list */ void removeLoop(struct Node* loop_node, struct Node* head) { struct Node* loopnode = loop_node; struct Node* curr = loop_node; // Count the number of nodes in loop unsigned int k = 1; while (loopnode->next != curr) { loopnode = loopnode->next; k++; } // Fix one pointer to head loopnode = head; // And the other pointer to k nodes after head curr= head; for (int i = 0; i < k; i++) curr = curr->next; /* Move both pointers at the same pace, they will meet at the loop’s starting node */ while (curr != loopnode) { loopnode = loopnode->next; curr = curr->next; } // Get pointer to the last node while (curr->next != loopnode) curr = curr->next; /* Set the next node of the loop ending node to null */ curr->next = NULL; } /* print linked list */ void printL(struct Node* node) { // Print the list after loop removal while (node != NULL) { cout << node->data << " "; node = node->next; } } struct Node* newNode(int key) { struct Node* temp = new Node(); temp->data = key; temp->next = NULL; return temp; } // Driver Code int main() { struct Node* head = newNode(55); head->next = newNode(45); head->next->next = newNode(54); head->next->next->next = newNode(63); head->next->next->next->next = newNode(56); /*loop for testing */ head->next->next->next->next->next = head->next->next; detectLoop(head); cout << "List after removing loop \n"; printL(head); return 0; }
Output Screenshot
Time Complexity
The time complexity of this approach is O(n).
Space Complexity
Since we are not using any additional space, the space complexity of this approach is O(1).
Using Hashing
- We can also try using a HashSet.
- We insert each node in the hash set, before inserting, we check if it is already present in the hash set, if yes, we set the next of last node in the cycle as null.
- If not, we insert that node in the hash set.
Java Code
import java.util.*; class LinkedList { static Node head; // head of list /* Node*/ static class Node { int val; Node next; Node(int d) { val = d; next = null; } } /* Inserts a new Node at front */ static public void push(int new_data) { Node new_node = new Node(new_data); //set the next of new node as head new_node.next = head; //set the value of head node as new_node head = new_node; } // print the linked list void printL(Node node) { while (node != null) { System.out.print(node.val + " "); node = node.next; } } // Returns true if the loop is removed else returns false. static boolean removeLoop(Node h) { HashSet<Node> set = new HashSet<Node>(); Node prev = null; while (h != null) { // If we already have this node it means there is a cycle and we // to remove this cycle set the next of // the previous node with null. if (set.contains(h)) { prev.next = null; return true; } // If we are seeing the node for // the first time, insert it in hash else { set.add(h); prev = h; h = h.next; } } return false; } /* Driver program */ public static void main(String[] args) { LinkedList llist = new LinkedList(); llist.push(55); llist.push(45); llist.push(54); llist.push(63); /*Create loop for testing */ llist.head.next.next.next.next = llist.head; if (removeLoop(head)) { System.out.println("Linked List after removing loop"); llist.printL(head); } else System.out.println("No Loop found"); } }
Output Screenshot
Python Code
# List Node class Node: def __init__(self, data, next=None): self.data = data self.next = next # nction to print list def printList(head): curr = head while curr: print(curr.data, end=" —> ") curr = curr.next print("None") # identify and remove cycle in a list using hashing def removeCycle(head): prev = None # previous pointer curr = head # main pointer # maintain a set to store visited nodes s = set() # traversing the list while curr: if curr in s: prev.next = None return # insert the current node into the set s.add(curr) # update the previous pointer to the current node and # move the main pointer to the next node prev = curr curr = curr.next if __name__ == '__main__': # total number of nodes in the linked list n = 5 # construct a linked list head = None for i in reversed(range(1, n + 2)): head = Node(i, head) # insert cycle head.next.next.next.next.next = head.next removeCycle(head) printList(head)
Output Screenshot
C++ Code
#include <bits/stdc++.h> using namespace std; struct Node { int val; struct Node* next; }; Node* newNode(int val) { Node* temp = new Node; temp->val = val; temp->next = NULL; return temp; } // function to print a list void printList(Node* head) { while (head != NULL) { cout << head->val << " "; head = head->next; } cout << endl; } // Function to detect and remove loop void hashAndRemove(Node* head) { // hash map hashes addresses of the list nodes unordered_map<Node*, int> node_map; // pointer to last node Node* last = NULL; while (head != NULL) { // insert node in the map if (node_map.find(head) == node_map.end()) { node_map[head]++; last = head; head = head->next; } // if node present in map already, there is a cycle, make the last node's next pointer // as NULL else { last->next = NULL; break; } } } /* Driver program */ int main() { Node* head = newNode(55); head->next = head; head->next = newNode(45); head->next->next = newNode(54); head->next->next->next = newNode(63); head->next->next->next->next = newNode(56); /* loop for testing */ head->next->next->next->next->next = head->next->next; // printList(head); hashAndRemove(head); printf(" List after removing loop \n"); printList(head); return 0; }
Output Screenshot
Time Complexity
The time complexity of this approach is O(n), where n is the number of nodes in the Linked List. Because we traverse the List entirely once while inserting each node in the Linked List.
Space Complexity
The space Complexity of this approach is O(n) because we are using extra space by taking the help of a Hash Set to store the nodes.
Conclusion
To conclude, in this article, we discussed how to find and remove loops in Linked Lists through several approaches. We discussed a naive approach and two efficient approaches and discussed their respective time and space complexities as well. We hope that you will now be able to solve this question along with it’s variations successfully.
People are also reading:
- Minimum Edit Distance
- WAP to Count no. of alphabets, digits and spaces present in a file
- Find maximum of minimum for every window size in a given array
- WAP to Print Triangle Using * Character
- Find ancestors of a given node in a Binary Tree
- Subset Sum Problem
- Clone a Singly Linked List
- Maximum size square sub-matrices with all 1’s
- Merge Sort for Linked Lists
- Convert a Binary Tree to Doubly Linked List
Leave a Comment on this Post