0

Given an array of integers and a number, I need to perform left rotations on the array and return the updated array to be printed as a single line of space-separated integers. I pass 7/9 checks, but some with large arrays fail because of time-out.

The time has to be <= 4 sec.

static int[] rotLeft(int[] a, int d) {
       int x = 0;
       while (x != d) {
           int first = a[0];
           int last = a[a.length - 1];
           for (int i = 0; i < a.length - 1; i++) {
               a[i] = a[i + 1];
               if (i == a.length - 2)
                   a[a.length - 2] = last;
               a[a.length - 1] = first;
           }
           x++;
       }
       return a;
   }
1

6 Answers 6

2

you're rotating only one position at a time, it is very slow, it is better to shift elements to appropriate places, for example:

static int[] rotLeft(int[] a, int d) {
    if (d == 0 || a == null || a.length == 0) {
        return a;
    }

    int[] b = new int[a.length];
    for (int i = 0; i < a.length; i++) {
        b[i] = a[(i + d) % a.length];
    }
    return b;
}
Sign up to request clarification or add additional context in comments.

2 Comments

Can you please explain this line? b[i] = a[(i + d) % a.length];
@JJKam it takes element from old array and shifts it into new array, as soon as i+d can be beyond array length, it has % to wrap around it
2

There are two things you could apply to this problem to improve the runtime.

  1. Ensure that d is less than a.length. If d is greater than a.length, then you are rotating elements past their original position and wasting cycles. An easy way to achieve this is with the modulus assignment operator (i.e., d %= a.length, which is equivalent to d = d % a.length).

  2. Elements should be shifted by the whole distance we are rotating, rather than shifting by one space each time. This allows us to perform the entire operation is 1 action, instead of in d action(s).

Applying these two principles would give us the following code:

static int[] rotLeft(int[] a, int d) {
  if (d < 0) {
    d = a.length - (-d % a.length);
  }

  d %= a.length;

  if (d == 0) {
    return a;
  }

  int first = a[0];

  int i = 0;
  int position = 0;

  while (i < a.length) {
    a[position] = a[(position + d) % a.length];

    position = (position + d) % a.length;
    i++;
  }

  a[a.length - d] = first;

  return a;
}

Comments

0

You make multiple passes, each time rotating by one place, which makes your program slow. There are at least 3 approaches to improve your program:

The third option performs rotation in place.

It may be worthwhile to microbenchmark all 3 to compare the speed for different input sizes

Comments

0

Of course you can repeat the rotation d times as you did in your sample code. But much faster would be if you would calculate the shift and do it in one go:

import static java.lang.System.*;

static int[] rotLeft( int[] a, int d ) 
{
    var len = a.length;
    var shift = d % len;
    var buffer = new int[len]; 
    arraycopy( a, shift, buffer, 0, len - shift );
    arraycopy( a, 0, buffer, len - shift, shift );
    arraycopy( buffer, 0, a, 0, len );
    return a;
}

Of course, instead of System.arraycopy() you can use a for-loop. If you are not forced to return a, you can omit the third call to arraycopy() and return buffer instead; this would leave the original array unchanged, too.

To output the array, try this:

var joiner = new StringJoiner( " " );
for( var v : a ) joiner.add( Integer.toString( v ) );
System.out.println( joiner.toString() );

3 Comments

1) Your buffer is bigger than needed, it only needs to be shift, not len. See e.g. my answer. --- 2) If you expect oversized values of d, why not expect negative values too?
@Andreas: negative numbers would be a right shift, but there are no "oversized numbers" in the specification: it says "left-rotate d times". The term d % a.length is just an optimisation. And regarding the buffer: why should I return the input array after it was modified in-place? Either the method returns void, or it should return buffer – but finally, I was lazy. But if really an in-place rotation is required, you are right, a buffer of size shift would do the job.
1) d % a.length is not an optimization, not for this answer's code. It is a requirement to prevent IndexOutOfBoundsException if d > a.length. --- 2) "why should I return the input array" But you are doing that. "it should return buffer" But you're not doing that. And since you are copying all of buffer back to a, to return it, the buffer is bigger than it needs to be, because the second arraycopy could be in-place in a, which is what I was saying.
0

For better performance, you should use the built-in array copy methods.

If you have to update the existing array, like your code is doing, I'd recommend doing it like this:

static int[] rotLeft(int[] a, int d) {
    if (a == null || a.length <= 1)
        return a; // nothing to rotate
    int shift = (d % a.length + a.length) % a.length; // normalize d
    if (shift == 0)
        return a; // no or full rotation(s)
    int[] t = Arrays.copyOfRange(a, 0, shift);
    System.arraycopy(a, shift, a, 0, a.length - shift);
    System.arraycopy(t, 0, a, a.length - shift, shift);
    return a;
}

If the returned array must be different, like this other answers do, I'd do it like this:

static int[] rotLeft(int[] a, int d) {
    if (a == null || a.length <= 1)
        return Arrays.copyOf(a, a.length); // nothing to rotate
    int shift = (d % a.length + a.length) % a.length; // normalize d
    if (shift == 0)
        return Arrays.copyOf(a, a.length); // no or full rotation(s)
    int[] t = new int[a.length];
    System.arraycopy(a, shift, t, 0, a.length - shift);
    System.arraycopy(a, 0, t, a.length - shift, shift);
    return t;
}

Both of the above solution allow d to exceed the size of the array, i.e. do more than a full rotation, and to use negative values, i.e. rotate right instead of left.

Test

System.out.println(Arrays.toString(rotLeft(new int[] { 1, 2, 3, 4, 5 }, 1)));
System.out.println(Arrays.toString(rotLeft(new int[] { 1, 2, 3, 4, 5 }, 3)));
System.out.println(Arrays.toString(rotLeft(new int[] { 1, 2, 3, 4, 5 }, 5)));
System.out.println(Arrays.toString(rotLeft(new int[] { 1, 2, 3, 4, 5 }, 7)));
System.out.println(Arrays.toString(rotLeft(new int[] { 1, 2, 3, 4, 5 }, -7)));

Output

[2, 3, 4, 5, 1]
[4, 5, 1, 2, 3]
[1, 2, 3, 4, 5]
[3, 4, 5, 1, 2]
[4, 5, 1, 2, 3]

Comments

0

You don't have to do the rotations one by one. If you rotate by d, the element at index i moves to index i - d when i >= d, and into index N + i - d when i < d. Makes sense?

int[] result = new int[a.length]
for (int i = 0; i < a.length; i++) {
    if (i < d) {
        result[a.length + i - d] = a[i];
    } else {
        result[i - d] = a[i];
    }
}
return result;

To handle the case where d >= a.length, you can add d = d % a.length as a pre-processing step.

4 Comments

This is very succinct and accurate. What do you imply by "rotations one by one"? Aren't you doing the same?
Your first implementation computes "d" full rotations of the array, one by one.
How did you manage to avoid getting an exception? E.g. if a.length=3, i=0 and d=9? The result would be -6. My tests are passed, but I don't know how did you escape it?
ah, that case is not covered... Most likely your tests don't exercise that case.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.