The problem involves verifying the presence of a closed loop within a sequence of connected nodes. Specifically, one must determine if traversing the next pointers eventually returns to a previously encountered node. While test environments may define connection indices for simulation purposse, the algorithm operates logically without reliance on these metadata values.
Efficient Strategy
The optimal solution utilizes a two-pointer technique known as Floyd's Cycle-Finding Algorithm. This approach ensures O(n) time complexity and O(1) space complexity by avoiding auxiliary storage structures like hash sets.
Core Logic
- Initialization: Create two iterators starting at the head of the list.
- Velocity Difference: One iterator advances one step per iterasion (
pointerA), while the other advances two steps (pointerB). - Termination Conditions:
- If the list is acyclic, the faster iterator will reach a null pointer first.
- If a cycle exists, the faster iterator will eventually "lap" or meet the slower iterator within the loop.
Implementation Considerations
- Enput Validation: An empty list or a single-node list cannot contain a cycle. Immediate return of false is required for these edge cases.
- Safety Checks: Before moving the faster pointer two steps ahead, verify that both the current node and its immediate successor are valid to prevent null dereference errors.
- Meeting Point: Equality between the two pointers confirms the existence of a cycle.
C++ Solution
#include <iostream>
using namespace std;
// Node structure definition
struct Node {
int data;
Node* link;
Node(int val) : data(val), link(nullptr) {}
};
class ListAnalyzer {
public:
bool verifyCircularity(Node* entryPoint) {
// Edge case: Not enough nodes to form a circle
if (entryPoint == nullptr || entryPoint->link == nullptr) {
return false;
}
Node* pointerA = entryPoint;
Node* pointerB = entryPoint;
// Traverse until pointerB hits the end safely
while (pointerB != nullptr && pointerB->link != nullptr) {
pointerA = pointerA->link; // Advance 1 step
pointerB = pointerB->link->link; // Advance 2 steps
// Collision indicates a loop
if (pointerA == pointerB) {
return true;
}
}
// Reached tail without collision
return false;
}
};
int main() {
// Case 1: Constructed with a loop
Node* n1 = new Node(5);
Node* n2 = new Node(10);
Node* n3 = new Node(15);
n1->link = n2;
n2->link = n3;
n3->link = n2; // n3 points back to n2, forming a cycle
ListAnalyzer analyzer;
cout << "Has Cycle: " << (analyzer.verifyCircularity(n1) ? "True" : "False") << endl;
// Case 2: Linear structure (no loop)
Node* x1 = new Node(1);
Node* x2 = new Node(2);
x1->link = x2;
// x2 links to nullptr by default
cout << "Has Cycle: " << (analyzer.verifyCircularity(x1) ? "True" : "False") << endl;
return 0;
}