Problem Statement
Given a linked list containing a loop, we aim to determine the starting point of the loop. The starting point is the node where the loop begins.
Examples
Example 1:
Input: list = [1, 2, 3, 4, 5, 6, 10]
Output: tail connects to index 2.

Example 2:
Input: 1->2->3->4->5
^ |
| |
+--------+
Output: 2
Explanation: The tailed of the linked list connects to the node at 1st index.
Example 3:
Input: 1->3->7->4->nullptr
Output: nullptr
Explanation: No loop is present in the linked list.
Different Approaches
1️⃣ Brute Force (Hashmap)
We can store nodes in a hash table so that, if a loop exists, the head will encounter the same node again. This node will be present in the table and hence, we can detect the loop.
Algorithm:
- Iterate the given list.
- For each node visited by the head pointer, check if the node is present in the hash table.
- If yes, the loop detected
- If not, insert the node in the hash table and move the head pointer ahead.
- If the head reaches null, then the given list does not have a cycle in it.
Code:
#include<bits/stdc++.h>
using namespace std;
class node {
public:
int num;
node* next;
node(int val) {
num = val;
next = NULL;
}
};
void insertNode(node* &head,int val) {
node* newNode = new node(val);
if(head == NULL) {
head = newNode;
return;
}
node* temp = head;
while(temp->next != NULL) temp = temp->next;
temp->next = newNode;
return;
}
void createCycle(node* &head,int pos) {
node* ptr = head;
node* temp = head;
int cnt = 0;
while(temp->next != NULL) {
if(cnt != pos) {
++cnt;
ptr = ptr->next;
}
temp = temp->next;
}
temp->next = ptr;
}
//process as per mentioned in solution
node* detectCycle(node* head) {
unordered_set<node*> st;
while(head != NULL) {
if(st.find(head) != st.end()) return head;
st.insert(head);
head = head->next;
}
return NULL;
}
int main() {
node* head = NULL;
insertNode(head,1);
insertNode(head,2);
insertNode(head,3);
insertNode(head,4);
insertNode(head,3);
insertNode(head,6);
insertNode(head,10);
createCycle(head,2);
node* nodeRecieve = detectCycle(head);
if(nodeRecieve == NULL) cout<<"No cycle";
else {
node* temp = head;
int pos = 0;
while(temp!=nodeRecieve) {
++pos;
temp = temp->next;
}
cout<<"Tail connects at pos "<<pos<<endl;
}
return 0;
}
OR:
#include <iostream>
#include <unordered_map>
// Definition of singly linked list
struct ListNode {
int val;
ListNode *next;
ListNode() : val(0), next(NULL) {}
ListNode(int data1) : val(data1), next(NULL) {}
ListNode(int data1, ListNode *next1) : val(data1), next(next1) {}
};
class Solution {
public:
// Function to find the starting point of the loop
// in a linked list using hashing technique
ListNode *findStartingPoint(ListNode *head) {
// Use temp to traverse the linked list
ListNode* temp = head;
// hashmap to store all visited nodes
std::unordered_map<ListNode*, int> mp;
// Traverse the list using temp
while(temp != nullptr) {
// Check if temp has been encountered again
if(mp.count(temp) != 0) {
// A loop is detected hence return temp
return temp;
}
// Store temp as visited
mp[temp] = 1;
// Move to the next node
temp = temp->next;
}
// If no loop is detected, return nullptr
return nullptr;
}
};
// Function to create a linked list with a loop for testing
void createTestList(ListNode* &head, int loop_start_val) {
ListNode* node1 = new ListNode(1);
ListNode* node2 = new ListNode(2);
ListNode* node3 = new ListNode(3);
ListNode* node4 = new ListNode(4);
ListNode* node5 = new ListNode(5);
node1->next = node2;
node2->next = node3;
node3->next = node4;
node4->next = node5;
head = node1;
// Create a loop from node5 to the specified node
ListNode* temp = head;
while (temp != nullptr && temp->val != loop_start_val) {
temp = temp->next;
}
node5->next = temp; // Creating the loop
}
int main() {
// Create a sample linked list with a loop
ListNode* head = nullptr;
createTestList(head, 2); // Creating a loop starting at node with value 2
// Create an instance of the Solution class
Solution solution;
// Detect the starting point of the loop in the linked list
ListNode* loopStartNode = solution.findStartingPoint(head);
if (loopStartNode) {
std::cout << "Loop detected. Starting node of the loop is: " << loopStartNode->val << std::endl;
} else {
std::cout << "No loop detected in the linked list." << std::endl;
}
// Clean up memory (free the allocated nodes)
ListNode* temp = head;
std::unordered_map<ListNode*, bool> visited;
while (temp != nullptr && visited[temp] == false) {
visited[temp] = true;
ListNode* next = temp->next;
delete temp;
temp = next;
}
return 0;
}
Complexity Analysis
- Time Complexity:
O(n)
- Iterating for the entire list once
- Space Complexity:
O(n)
- We store all nodes in hash table.
2️⃣ Slow and Fast Pointer Approach
Intuition:
- Detecting the Loop:
- Two Pointer Approach: We use two pointers,
slow
andfast
, both starting at the head of the linked list. - Movement of Pointers: The
slow
pointer moves one step at a time, while thefast
pointer moves two steps at a time. - Meeting Point: If there is a loop in the linked list, these two pointers will eventually meet within the loop. This happens because the
fast
pointer, moving at double the speed of theslow
pointer, will "lap" theslow
pointer inside the loop. - Finding the Loop's Start: Once a loop is detected, reset the slow pointer to the head of the linked list. Then, move both the fast and slow pointers one step at a time. The point where they meet again is identified as the starting point of the loop. This method ensures efficient detection and pinpointing of the loop's starting location in the linked list.
- Two Pointer Approach: We use two pointers,
- Why They Meet:
- Imagine two runners on a circular track: one running twice as fast as the other. The faster runner will eventually catch up to the slower runner, regardless of where they start. This meeting point within the loop confirms the presence of a cycle.
Visualization:
Consider a linked list with a loop:
A -> B -> C -> D -> E -> F
^ |
|______________|
In this linked list:
- The loop starts at node
C
. - The nodes before the loop are
A
andB
. - The loop is formed by nodes
C -> D -> E -> F -> C
.
Step-by-Step Breakdown:
1 Initialization:
slow
andfast
pointers both start atA
(head of the list).
2 Moving Pointers to Detect Loop:
- Move
slow
by one step andfast
by two steps in each iteration:
Iteration | slow Position | fast Position |
---|---|---|
1 | A (start) | A (start) |
2 | B | C |
3 | C | E |
4 | D | C |
5 | E | E (meet) |
- Meeting Point:
slow
andfast
meet at nodeE
, confirming a loop exists.
3 Finding the Start of the Loop:
- Reset
slow
toA
(head of the list). - Move both
slow
andfast
one step at a time:
Iteration | slow Position (from Head) | fast Position (from Meeting Point) |
---|---|---|
1 | A | E |
2 | B | F |
3 | C (meet) | C (meet) |
- Start of the Loop:
slow
andfast
both meet at nodeC
, which is the start of the loop.
Algorithm:
- Initially take two pointers, fast and slow. The fast pointer takes two steps ahead while the slow pointer will take a single step ahead for each iteration.
- We know that if a cycle exists, fast and slow pointers will collide.
- If the cycle does not exist, the fast pointer will move to
NULL
. - Else, when both slow and fast pointer collides, it detects a cycle exists.
- Take another pointer, say entry. Point to the very first of the linked list.
- Move the slow and the entry pointer ahead by single steps until they collide.
- Once they collide we get the starting node of the linked list.
Dry Run:
Initial State:
A (Head) -> B -> C -> D -> E -> F -> G
^ |
|______________|
Slow = A, Fast = A
1 After 1st Move:
A -> B (slow) -> C (fast) -> D -> E -> F -> G
^ |
|_____________________|
2 After 2nd Move:
A -> B -> C (slow) -> D -> E (fast) -> F -> G
^ |
|_____________________|
3 After 3rd Move:
A -> B -> C -> D (slow) -> E -> F -> G
(fast)
^ |
|_____________________|
4 After 4th Move (Got the Loop):
A -> B -> C -> D -> E (slow | fast) -> F -> G
^ |
|____________________________|
5 Reinitialize the slow to head:
A (slow) -> B -> C -> D -> E (fast) -> F -> G
^ |
|_____________________|
6 Move the slow and fast by one (First Move):
A -> B (slow) -> C -> D -> E -> F (fast) -> G
^ |
|_____________________|
7 Second Move:
A -> B -> C (slow) -> D -> E -> F -> G (fast)
^ |
|______________|
8 Third Move:
A -> B -> C -> D (slow | fast) -> E -> F -> G
^ |
|____________________________|
Return D
Code:
#include <iostream>
using namespace std;
//Definition of singly linked list:
struct ListNode
{
int val;
ListNode *next;
ListNode()
{
val = 0;
next = NULL;
}
ListNode(int data1)
{
val = data1;
next = NULL;
}
ListNode(int data1, ListNode *next1)
{
val = data1;
next = next1;
}
};
class Solution {
public:
//Function to find the first node
//Of the loop in a linked list
ListNode* findStartingPoint(ListNode* head) {
//Initialize a slow and fast
//Pointers to the head of the list
ListNode* slow = head;
ListNode* fast = head;
// Phase 1: Detect the loop
while (fast != NULL && fast->next != NULL) {
// Move slow one step
slow = slow->next;
// Move fast two steps
fast = fast->next->next;
// If slow and fast meet,
// a loop is detected
if (slow == fast) {
// Reset the slow pointer
// To the head of the list
slow = head;
// Phase 2: Find the first node of the loop
while (slow != fast) {
// Move slow and fast one step
// At a time
slow = slow->next;
fast = fast->next;
// When slow and fast meet again,
// It's the first node of the loop
}
// Return the first node of the loop
return slow;
}
}
//If no loop is found,
//Return NULL
return NULL;
}
};
int main() {
// Create a sample linked list with a loop
ListNode* node1 = new ListNode(1);
ListNode* node2 = new ListNode(2);
node1->next = node2;
ListNode* node3 = new ListNode(3);
node2->next = node3;
ListNode* node4 = new ListNode(4);
node3->next = node4;
ListNode* node5 = new ListNode(5);
node4->next = node5;
// Make a loop from node5 to node2
node5->next = node2;
// Set the head of the linked list
ListNode* head = node1;
// Detect the loop in the linked list
Solution sol;
ListNode* loopStartNode = sol.findStartingPoint(head);
if (loopStartNode) {
cout << "Loop detected. Starting node of the loop is: " << loopStartNode->val << endl;
} else {
cout << "No loop detected in the linked list." << endl;
}
// Clean up memory (free the allocated nodes)
delete node1;
delete node2;
delete node3;
delete node4;
delete node5;
return 0;
}
Complexity Analysis:
- Time Complexity:
O(n)
- The code examines each node in the linked list exactly once, where
n
is the total number of nodes. This results in a linear time complexity,O(n)
.
- The code examines each node in the linked list exactly once, where
- Space Complexity:
O(1)
- The code uses a fixed amount of extra space, regardless of the size of the linked list.