The Circular Array Loop problem asks you to determine whether a given circular array of integers contains a cycle that meets specific requirements.
nums
of length n
, where each element is a nonzero integer. Positive numbers move you forward, negative numbers move you backward.i
and following the jumps indefinitely.True
if there is at least one such cycle; otherwise, return False
.
Constraints:
nums.length
≤ 5000nums[i]
≠ 0 for all i
The problem at first seems like a classic cycle-detection problem, similar to those involving linked lists. However, the movement is dictated by the values in the array, and the array is circular. A brute-force approach would be to simulate the process from every index, but this would be inefficient.
The main challenges are:
Our goal is to efficiently detect a valid cycle as defined by the problem. Here is the step-by-step approach:
next == current
), skip this path since cycles must be longer than one element.
This approach ensures we only check each path once and avoid unnecessary repeated work, making the algorithm efficient.
Consider nums = [2, -1, 1, 2, 2]
:
Brute-force approach: For each starting index, simulate the process, which could take up to O(n) time per index, for a total of O(n^2).
Optimized approach (with marking):
The Circular Array Loop problem is a specialized cycle-detection challenge with constraints on direction and length. By adapting Floyd's cycle detection algorithm and marking visited elements, we can efficiently check for valid cycles in O(n) time and O(1) space. The key insights are handling circular indexing, ensuring direction consistency, and avoiding repeated work by marking visited paths.
class Solution:
def circularArrayLoop(self, nums):
n = len(nums)
def next_index(i):
return (i + nums[i]) % n
for i in range(n):
if nums[i] == 0:
continue
slow, fast = i, i
direction = nums[i] > 0
while True:
# Move slow pointer one step
next_slow = next_index(slow)
# Move fast pointer two steps
next_fast = next_index(fast)
if nums[next_fast] * nums[fast] <= 0 or nums[next_fast] == 0:
break
next_fast = next_index(next_fast)
if nums[next_fast] * nums[fast] <= 0 or nums[next_fast] == 0:
break
# Check direction
if nums[next_slow] * nums[i] <= 0 or nums[next_fast] * nums[i] <= 0:
break
slow = next_slow
fast = next_fast
if slow == fast:
if slow == next_index(slow):
break # one-element loop
return True
# Mark all nodes in the current path as 0
j = i
while nums[j] * nums[i] > 0:
next_j = next_index(j)
nums[j] = 0
j = next_j
return False
class Solution {
public:
bool circularArrayLoop(vector<int>& nums) {
int n = nums.size();
auto next = [&](int i) {
return (i + nums[i] % n + n) % n;
};
for (int i = 0; i < n; ++i) {
if (nums[i] == 0) continue;
int slow = i, fast = i;
bool dir = nums[i] > 0;
while (true) {
int next_slow = next(slow);
int next_fast = next(fast);
if (nums[next_fast] * nums[fast] <= 0 || nums[next_fast] == 0) break;
next_fast = next(next_fast);
if (nums[next_fast] * nums[fast] <= 0 || nums[next_fast] == 0) break;
if (nums[next_slow] * nums[i] <= 0 || nums[next_fast] * nums[i] <= 0) break;
slow = next_slow;
fast = next_fast;
if (slow == fast) {
if (slow == next(slow)) break;
return true;
}
}
// Mark visited
int j = i;
while (nums[j] * nums[i] > 0) {
int next_j = next(j);
nums[j] = 0;
j = next_j;
}
}
return false;
}
};
class Solution {
public boolean circularArrayLoop(int[] nums) {
int n = nums.length;
for (int i = 0; i < n; i++) {
if (nums[i] == 0) continue;
int slow = i, fast = i;
boolean dir = nums[i] > 0;
while (true) {
int nextSlow = ((slow + nums[slow]) % n + n) % n;
int nextFast = ((fast + nums[fast]) % n + n) % n;
if (nums[nextFast] * nums[fast] <= 0 || nums[nextFast] == 0) break;
nextFast = ((nextFast + nums[nextFast]) % n + n) % n;
if (nums[nextFast] * nums[fast] <= 0 || nums[nextFast] == 0) break;
if (nums[nextSlow] * nums[i] <= 0 || nums[nextFast] * nums[i] <= 0) break;
slow = nextSlow;
fast = nextFast;
if (slow == fast) {
if (slow == ((slow + nums[slow]) % n + n) % n) break;
return true;
}
}
// Mark visited
int j = i;
while (nums[j] * nums[i] > 0) {
int nextJ = ((j + nums[j]) % n + n) % n;
nums[j] = 0;
j = nextJ;
}
}
return false;
}
}
var circularArrayLoop = function(nums) {
const n = nums.length;
const nextIndex = (i) => ((i + nums[i]) % n + n) % n;
for (let i = 0; i < n; i++) {
if (nums[i] === 0) continue;
let slow = i, fast = i;
const dir = nums[i] > 0;
while (true) {
let nextSlow = nextIndex(slow);
let nextFast = nextIndex(fast);
if (nums[nextFast] * nums[fast] <= 0 || nums[nextFast] === 0) break;
nextFast = nextIndex(nextFast);
if (nums[nextFast] * nums[fast] <= 0 || nums[nextFast] === 0) break;
if (nums[nextSlow] * nums[i] <= 0 || nums[nextFast] * nums[i] <= 0) break;
slow = nextSlow;
fast = nextFast;
if (slow === fast) {
if (slow === nextIndex(slow)) break;
return true;
}
}
// Mark visited
let j = i;
while (nums[j] * nums[i] > 0) {
let nextJ = nextIndex(j);
nums[j] = 0;
j = nextJ;
}
}
return false;
};