Detect Loop in a Linked List | And Remove Loop

Problem
Given the head pointer (head) of a linked list, check whether there’s a loop in the linked list. Return true if a loop is present and false otherwise.

Detect Loop

Naive Approach

We could maintain a hashmap (hmap) storing the node address of each node as we traverse the linked list. If we reach a node which was already present in the hmap return true and return false if we reach NULL.

Pseudocode

DetectLoop (head):
node = head
while (node != NULL):
  if (map.find[node]):
    return true
  map[node] = 1
  node = node->next
return false
Optimised Approach: Floyd’s Cycle-Finding (tortoise and hare) Algorithm

The idea is to have two head pointers to the given linked list and move them at different speeds. Move one pointer (ptr1) forward by one node and second pointer (ptr2) by two nodes. Now two things could happen:

  • If the linked list has a loop, the two pointers will definitely meet somewhere inside the loop. This is because they are moving at different speeds, in a same circular path.
  • Else either of the two pointers will become null.

Time Complexity: O(N), N is the length of the linked list

Code Implementation

bool hasLoop (Node *head) {

    if (head == NULL || head->next == NULL) {
        return false;
    }
    
    Node *ptr1 = head->next, *ptr2 = head->next->next;
     
    while (ptr1 != NULL && ptr2 != NULL && ptr2->next != NULL) {
        
        if (ptr1 == ptr2) {
            return true;
        }
        
        ptr1 = ptr1->next;
        ptr2 = ptr2->next->next;
    }
     
    return false;
}
Remove Loop

In order to remove the loop, we need the count of the number of nodes in the loop, let it be k.

Pseudocode

1. Let ptr1 = head, ptr2 = kth node from the head
2. Move ptr1 and ptr2 by 1 node each
3. ptr1 and ptr2 will meet at the starting node of the loop
4. Set the next of the meeting node to NULL

Code Implementation

void removeLoop (Node *head, Node *ptr) {

    Node *ptr1 = head, *ptr2 = head;
    int k = findLoopLength (ptr);
    
    for (int i=0; i<k && ptr2 != NULL; i++) {
        ptr2 = ptr2->next;
    }
     
    while (ptr1 != ptr2) {
        ptr1 = ptr1->next;
        ptr2 = ptr2->next;
    }
    
    while (ptr2->next != ptr1) {
        ptr2 = ptr2->next;
    }
    
    ptr2->next = NULL;
}

bool detectAndRemoveLoop (Node *head) {
    
    if (head == NULL || head->next == NULL) {
        return false;
    }
    
    Node *ptr1 = head->next, *ptr2 = head->next->next;
     
    while (ptr1 != NULL && ptr2 != NULL && ptr2->next != NULL) {
        
        if (ptr1 == ptr2) {
            removeLoop(head, ptr1);
            return true;
        }
        
        ptr1 = ptr1->next;
        ptr2 = ptr2->next->next;
    }
     
    return false;
}
How does Floyd’s Cycle-Finding Algorithm work? (Proof)

Here’s the explanation for why the two pointers (ptr1 & ptr2) meet at the starting node of the loop:

Case 1: (c > k)

Now, to detect the starting point of loop, let’s assume ptr1 and ptr2 meet at a point x:

Initially,
ptr1 = 0, ptr2 = k

ptr2 after c-k + k (1 round in the loop) = c steps, 
would be at the starting point of the loop.

And ptr1 after c steps would also be at the starting point of the loop ie. x.

Hence both ptr1 and ptr2 would travel c distance to meet at the starting point of the loop.

Case 2: (c < k)

Initially,
ptr1 = 0, ptr2 = k

ptr2 after k-t steps would be at the starting point of the loop.
Since,
k - t = k - (k-c) = c steps

And ptr1 after c steps would also be at starting point of the loop ie. x.

Hence both ptr1 and ptr2 would travel c distance to meet at the starting point of the loop.

Here’s the complete code:

//
//  main.cpp
//  Detect and Remove Loop (Linked List)
//
//  Created by Himanshu on 11/07/22.
//

#include <iostream>
using namespace std;
 
struct node {
    int info = 0;
    struct node *next;
};
typedef struct node Node;
 
node* newNode(int data) {
    node* Node = new node();
    Node->info = data;
    Node->next = NULL;
  
    return(Node);
}
 
void printLinkedList (Node* head) {
    Node *x = head;
     
    while (x != NULL) {
        cout<<(x->info)<<" ";
        x = x->next;
    }
    cout<<endl;
}
 
Node* insertNode (Node *head, Node* x) {
    Node *node = head;
    while (node->next != NULL) {
        node = node->next;
    }
    x->next = NULL;
    node->next = x;
    return head;
}
 
int findLoopLength (Node *head) {
    int len = 1;
    Node *node = head;
    
    while (node != NULL && node->next != head) {
        len++;
        node = node->next;
    }
    
    return len;
}
 
void removeLoop (Node *head, Node *ptr) {
    
    Node *ptr1 = head, *ptr2 = head;
    int k = findLoopLength (ptr);
    
    for (int i=0; i<k && ptr2 != NULL; i++) {
        ptr2 = ptr2->next;
    }
     
    while (ptr1 != ptr2) {
        ptr1 = ptr1->next;
        ptr2 = ptr2->next;
    }
    
    while (ptr2->next != ptr1) {
        ptr2 = ptr2->next;
    }
    
    ptr2->next = NULL;
}

bool detectAndRemoveLoop (Node *head) {
    
    if (head == NULL || head->next == NULL) {
        return false;
    }
    
    Node *ptr1 = head->next, *ptr2 = head->next->next;
     
    while (ptr1 != NULL && ptr2 != NULL && ptr2->next != NULL) {
        
        if (ptr1 == ptr2) {
            removeLoop(head, ptr1);
            return true;
        }
        
        ptr1 = ptr1->next;
        ptr2 = ptr2->next->next;
    }
     
    return false;
}


 
int main() {
    Node *head1 = newNode(1);
     
    head1 = insertNode (head1, newNode(2));
    
    Node* loopHead = newNode(3);
    head1 = insertNode (head1, loopHead);
    
    head1 = insertNode (head1, newNode(4));
    
    Node* loopTail = newNode(5);
    head1 = insertNode (head1, loopTail);
    
    loopTail->next = loopHead;
     
    Node *head2 = newNode(6);
    head2 = insertNode (head2, newNode(7));
    head2 = insertNode (head2, newNode(8));
     
    if (detectAndRemoveLoop(head1)) {
        cout<<"Linked List 1 has a loop:"<<endl;
        printLinkedList(head1);
    } else {
        cout<<"Linked List 1 does not have a loop:"<<endl;
        printLinkedList(head1);
    }
     
    if (detectAndRemoveLoop(head2)) {
        cout<<"Linked List 2 has a loop:"<<endl;
        printLinkedList(head2);
    } else {
        cout<<"Linked List 2 does not have a loop:"<<endl;
        printLinkedList(head2);
    }
     
    return 0;
}
Output
Linked List 1 has a loop:
1 2 3 4 5 
Linked List 2 does not have a loop:
6 7 8 

Leave a Reply

Your email address will not be published. Required fields are marked *