Hmmm, very interesting little problem indeed. Here is an evil approach:
A little thought will convince you that you can scan the array in O(n) time
and know exactly the target index (t_i) for each array element a_i. The
question is, how to store that information (a_i, t_i), i.e. two numbers, in
the array a[] (that hold one number in each element)?
Well, we can use the value M = max(|a_i|)+1 (- the max value is again the by
-product of a previous linear scan), and, rewrite
the array value a[i] to be (t_i+1)*M + a_i.
Thus, t_i = a[i]/M-1, a_i = a[i] % M. So relax, we can get the two values
back easily.
Now we can use the following method to replace old with new:
int start=0;
while ( start != end of array ) {
k=start;
p = a[k];
while ( a[k] is not in the right place, i.e. a[k]>=M) {
let t_i = the target of p
save temp = a[t_i];
insert p into right place t_i;
k = the target of temp ;
p = temp;
}
start ++;
}
This is a nested loop but is O(n) time. The inner loop basically cycles
through the chain of replaced values until the chain goes back to the
beginning, so each value is replaced only once and never will be in the
inner loop again.
Of course, if the M value trick causes number overflow (i.e. (n+1)*M is too
big), this method won't work. So this is really just an evil hack, nothing
more.