Monotonic Stack Horizon — Problem Statement & Solution Guide
Problem Description
Given a singly linked list of integers, determine the length of the cycle if one exists. If the list is acyclic, return 0. The cycle is defined as a path where a node's next pointer eventually points back to a previously visited node, forming a closed loop. You must solve this in O(1) space complexity without modifying the list structure.
The input is provided as a head pointer to the linked list. The output is a single integer representing the number of nodes in the cycle. If no cycle is detected, the output must be 0. Note that the list may contain duplicate values, but the cycle detection must rely solely on node references, not values.
DSA Pattern Breakdown
DSA Pattern Breakdown
"Monotonic Stack Horizon"
WHY DOES IT MATTER?
The two‑pointer (fast‑slow) pattern is a cornerstone for many linked‑list problems—cycle detection, palindrome checking, and middle‑node finding—because it provides deterministic results without auxiliary storage, a crucial trait for memory‑constrained environments.
OPTIMIZATION CHALLENGE
The key insight is that relative speed creates a guaranteed meeting point in any finite loop, allowing you to transform an O(N) space hash‑set solution into an O(1) space solution while preserving linear time.
REAL-WORLD CONNECTION
Think of a train on a circular track (the hare) and a slower maintenance vehicle (the tortoise); the faster train will inevitably catch up to the slower one if the track loops, mirroring how pointers converge in a cyclic list.
During an interview, first write the detection phase, then immediately reuse the meeting point to compute the cycle length—don’t introduce extra variables or data structures; keep the code tight and comment the invariant that hare moves twice as fast.
COMPLEXITY AT A GLANCE
O(N)O(1)Core Theory — Why This Approach?
The classic solution to detecting a cycle in a singly linked list leverages Floyd's Tortoise and Hare algorithm, also known as the two‑pointer technique. By advancing one pointer (the hare) twice as fast as the other (the tortoise), any loop forces the faster pointer to eventually lap the slower one, guaranteeing a meeting point if a cycle exists. Once a meeting occurs, the length of the cycle can be measured by fixing one pointer at the meeting node and moving the other around the loop until it returns, counting the steps. Naïve methods—such as marking visited nodes with a hash set or modifying node values—require O(N) extra space or alter the input, which violates the O(1) space constraint and can be prohibitive for massive lists where memory overhead is critical. The optimal paradigm thus combines constant‑space pointer arithmetic with deterministic traversal, delivering linear time performance while preserving the original list structure.
Interview Questions on This Problem
Q1How would you modify Floyd's algorithm to also return the starting node of the cycle, not just its length?
After detecting a meeting point, reset one pointer to the head and advance both pointers one step at a time; they will meet at the cycle's entry node because the distance from head to entry equals the distance from meeting point to entry.
Q2In a fintech transaction ledger implemented as a linked list, why is detecting a cycle important, and how does O(1) space help?
A cycle could indicate corrupted references leading to infinite processing of transactions; using O(1) space ensures the detection runs efficiently even on massive ledgers where allocating extra memory per transaction is infeasible.
Q3A startup asks you to detect a cycle in a distributed singly linked list where nodes reside on different machines. What additional challenges arise, and how would you adapt the algorithm?
Network latency and partial failures require you to treat pointer hops as remote calls; you would still use the two‑pointer approach but batch hops to reduce round‑trips and incorporate timeout/retry logic, ensuring the algorithm remains O(1) in local memory while handling distributed state.
Examples
Input
head = [3, 2, 0, -4], tail connects to index 1
Output
3
Explanation: The list is 3 -> 2 -> 0 -> -4 -> 2. The cycle starts at node 2 and includes nodes 2, 0, and -4. The length of the cycle is 3.
Input
head = [1, 2], tail connects to index 0
Output
2
Explanation: The list is 1 -> 2 -> 1. The cycle includes nodes 1 and 2. The length of the cycle is 2.
Input
head = [1, 2, 3, 4, 5], no cycle
Output
0
Explanation: The list is 1 -> 2 -> 3 -> 4 -> 5 -> null. There is no cycle, so the output is 0.
Input
head = [5], tail connects to index 0
Output
1
Explanation: The list is 5 -> 5. The cycle includes only node 5. The length of the cycle is 1.
Constraints
- 0 <= number of nodes in the list <= 10^5
- -10^5 <= node.val <= 10^5
- The list is guaranteed to be either acyclic or have exactly one cycle
- The cycle, if present, does not include the head node unless the head is part of the loop
Optimal Approach & Strategy
Use two pointers moving at different speeds to detect a meeting point, then traverse the loop once more to count its length, all without extra memory.
Brute Force Approach
Store every visited node in a hash set and stop when you encounter a node already in the set; the cycle length is the number of steps since its first occurrence.
Verified Code Solutions
function solution(nums) {
let stack = [];
let result = 0;
for (let num of nums) {
while (stack.length > 0 && stack[stack.length - 1] < num) {
result += stack.pop();
}
stack.push(num);
}
while (stack.length > 0) {
result += stack.pop();
}
return result;
}class Solution {
public:
int solution(vector<int>& nums) {
vector<int> stack;
int result = 0;
for (int num : nums) {
while (!stack.empty() && stack.back() < num) {
result += stack.back();
stack.pop_back();
}
stack.push_back(num);
}
while (!stack.empty()) {
result += stack.back();
stack.pop_back();
}
return result;
}
}class Solution {
public int solution(int[] nums) {
int[] stack = new int[nums.length];
int top = -1;
int result = 0;
for (int num : nums) {
while (top >= 0 && stack[top] < num) {
result += stack[top--];
}
stack[++top] = num;
}
while (top >= 0) {
result += stack[top--];
}
return result;
}
}def solution(nums):
stack = []
result = 0
for num in nums:
while stack and stack[-1] < num:
result += stack.pop()
stack.append(num)
while stack:
result += stack.pop()
return resultfunction solution(nums) {
let stack = [];
let result = 0;
for (let num of nums) {
while (stack.length > 0 && stack[stack.length - 1] < num) {
result += stack.pop();
}
stack.push(num);
}
while (stack.length > 0) {
result += stack.pop();
}
return result;
}Asked in Top Tech Interviews
Solve in Interative Editor
Ready to test your code? Open our built-in compiler, run custom test suites, and see detailed complexity analysis reports instantly.