I'm not sure how you're defining "better", but perhaps this qualifies. At least it's different from your solution and the solutions to the linked list questions (pun intended).
If we make a path
n+1 --> array[n+1] --> array[array[n+1]] --> ...
then this path contains a cycle if and only if array^k[n+1] = array^l[n+1]
for some k != l
, that is, if and only if there is a repeat. The question now becomes a common linked list problem that can be solved as follows.
Start two particles on the first node. Let the first particle move at unit speed and let the second particle move at twice unit speed. Then if there is a cycle, the second particle will end up looping back behind the first, and eventually they'll be the same. Why? Well, if you think of the particles as on a circle (which they will be once the find the loop), at every time unit the second particle gets one directed step closer to the first. Therefore they must eventually collide. One they do, you've found a loop. To find the repeated value, simply get the length of the loop by letting one particle stand still while the other runs the loop again. Then start both particles at the start again, let one move the length of the loop ahead, and then run both particles together with constant distance between them until they meet again at the beginning of the loop.
As some commentators are outraged that I did not include all of the details of how to find a loop in a linked list, here it now is. No promises that this isn't buggy (it's Matlab-esque pseudocode after all), but it should at least explain the idea.
%% STEP 1: find a point in the cycle of the linked list using a slow and fast particle
slow = n+1;
fast = n+1;
for i=1 to n+1
slow = array[slow];
fast = array[array[fast]];
if (slow == fast)
break;
end
%% STEP 2: find the length of the cycle by holding one particle fixed
length = 1;
fast = array[fast]
while fast != slow
fast = array[fast];
length = length+1;
end
%% STEP 3: find the repeated element by maintaining constant distance between particles
slow = n+1;
fast = n+1;
for i=1 to length
fast = array[fast];
end
while fast != slow
fast = array[fast];
slow = array[slow];
end
%% STEP 4: return the repeated entry
return slow;
Here I started at n+1
because array[i]
is between 1 and n, so neither particle will ever be sent back to n+1
. This makes at most one pass through the array (though not in order) and keeps track of two particles (slow and fast) and one integer (length). The space is therefore O(1) and the time is O(n).