1

I can't wrap my head around my solution for the problem:

A zero-indexed array A consisting of N integers is given. Rotation of the array means that each element is shifted right by one index, and the last element of the array is also moved to the first place.

For example, the rotation of array A = [3, 8, 9, 7, 6] is [6, 3, 8, 9, 7]. The goal is to rotate array A K times; that is, each element of A will be shifted to the right by K indexes.

I wanted to create solution without creating new array, but just modifying the one in place. It works... most of the time. Example tests pass, and other also pass, but some, for which Codility doesn't show the input, fail.

public int[] solution(int[] A, int K) {
    for (var i = 0; i < A.Length - 1; i++) {
        var destIndex = (i * K + K) % A.Length;
        var destValue = A[destIndex];
        A[destIndex] = A[0];
        A[0] = destValue;
    }
    return A;
}

I've skipped the code related to the fact that you don't need to rotate whole array few times (ie. rotating by A.Length % K is enough).

What's wrong with my implementation? Am I missing some corner case?

Community
  • 1
  • 1
Look
  • 11
  • 2
  • In-place shiftin by `K` indexes with O(n) time is a bit trickier than that. Try running some test inputs and you will see that by simply swapping elements like that you will not end up with what you are expecting. A very good (and correct) way of doing this is explained here: https://stackoverflow.com/questions/876293/fastest-algorithm-for-circle-shift-n-sized-array-for-m-position – dvaergiller Jul 26 '18 at 14:10
  • Thanks @dvaergiller for the link, I've found there an answer that made me realize it's the greatest common divisor that I'm missing - my solution only worked if the GCD was 1. I've posted corrected solution – Look Jul 26 '18 at 20:19

5 Answers5

0

The algorithm for this should be very simple, like:

aux <- array[array.Length - 1]

for index = 0, array.Length - 2 do
    array[index + 1] <- array[index]
    array[0] <- aux
endfor

Note, that in the special cases when array.Length <= 1, you do not need anything to achieve the rotation. If you want to achieve K rotations without an array, you can call this K times.

You will need to be tricky to achieve an optimal algorithm. Here I will take the following approach. An array's given element can have three different possible states. I explain it through the example. If I put the K'th element into a variable called aux and place the 0'th element in its place, then we have the following three states:

  • at element 0 the original element was already moved to another place, but the final element did not arrive yet. This is the moved state
  • at element K the original element was already moved and the final element already arrived there. This is the arrived state
  • at element 2 * K we did nothing so far, so there we have the original state

So, if we can mark somehow the elements, then the algorithm would look like this:

arrivedCount <- 0 //the number of arrived elements is counted in order to make sure we know when we need to search for an element with an original state

    index <- 0
    N <- array.Length
    aux <- array[index]
    mark(array[index], moved)
    index <- (index + K) mod N
    while arrivedCount < N do
        state <- array[index]
        if (state = moved) then
            array[index] <- aux
            arrivedCount <- arrivedCount + 1
            mark(array[index], arrived)
            if arrivedCount < N then
                while (state(array[index]) <> original) do
                    index <- (index + 1) mod N
                endwhile
                aux <- array[index]
                mark(array[index], moved)
                index <- (index + K) mod N
            endif
        else //it is original
            aux2 <- array[index]
            array[index] <- aux
            aux <- aux2
            arrivedCount <- arrivedCount + 1
            mark(array[index], arrived)
            index <- (index + K) mod N
        endif
    endwhile

Now, how could we use this in practice? Let's consider the example when your array only has positive numbers as value. You mark all elements at start by assigning them their negative value (-5 instead of 5, for example). Whenever a state is modified to move, it will have a value of 0 and whenever it is arrived, you will have the positive number. It is up to you to define how you can mark such elements and you will need to do this in conformity with your task. If you are unable to mark the elements for any reason, then you will need to create an auxiliary array in order to solve this.

EDIT

Do not be afraid of the while, it should not search for too many steps because of the modulo classes. An implementation in Javascript:

var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var arrivedCount = 0;
var index = 0;
var N = array.length;
var K = 3;
for (var i = 0; i < array.length; i++) array[i] = -array[i];
var aux, aux2, state;
aux = array[index];
array[index] = 0;
index = (index + K) % N;
var step = 0
while ((arrivedCount < N) && (++step < 1000)) {
    if (array[index] === 0) {
        array[index] = -aux;
        arrivedCount++;
        if (arrivedCount < N) {
            while (array[index] >= 0) index = (index + 1) % N;
            aux = array[index];
            array[index] = 0;
            index = (index + K) % N;
        }
    } else {
        aux2 = array[index];
        array[index] = -aux;
        aux = aux2;
        arrivedCount++;
        index = (index + K) % N
    }
}

Change the definition of array and K according to your will.

Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175
  • 1
    Hm, applying your second algorithm by hand with `N = 5` and `K = 2` gives me the result `[2, 3, 4, 1, 0]` which is wrong. Am I completely off here? I can not see how either this or the asker's implementations could work. – dvaergiller Jul 26 '18 at 13:58
  • My answer obviously doesn't... I've tried it on some input that worked, but got lucky, now I see how flawed it is. The codility input example is `3 8 9 7 6` with K = 3 and @Lajos Arpad second answer doesn't work. The multiplied rotation by 1 is of course trivial. I look for correct second one. – Look Jul 26 '18 at 14:14
  • @dvaergiller you are not off at all. There was a mistake in my answer and I have removed it, I will edit my answer with a better approach shortly. – Lajos Arpad Jul 26 '18 at 17:09
  • @Look yes, my second approach was incorrect. I will edit my answer soon. – Lajos Arpad Jul 26 '18 at 17:09
  • @dvaergiller now the answer is fixed, thank you for pointing out the problem, I was in a hurry when I answered the question and did not have time to deeply think about the problem. – Lajos Arpad Jul 26 '18 at 18:15
0

I've finally managed to find out what's wrong with my solution thanks to @dvaergiller who posted the question with a similar to mine approach: Fastest algorithm for circle shift N sized array for M position

This answer made me realize my solution is failing every time the greatest common divisor of A.Length and K is not 1. @IsaacTurner solution is much easier to understand, and also shows there's no need to constantly switch places of elements, but now I see I can correct my solution.

I basically should not go through all elements in the array to find correct place for every one of them, because if the greatest common divisor is not 1 I'll start switching elements again. Instead it must be stopped as soon as full cycle is made and restarted to start switching based on next position.

Here's corrected version of my solution:

int gcd(int a, int b) => b == 0 ? a : gcd(b, a % b);

public int[] solution(int[] A, int K)
{
    for (var i = 0; i < gcd(A.Length, K); i++)
    {
        for (var j = i; j < A.Length - 1; j++)
        {
            var destIndex = ((j-i) * K + K + i) % A.Length;
            if (destIndex == i) break;
            var destValue = A[destIndex];
            A[destIndex] = A[i];
            A[i] = destValue;
        }
    }

    return A;
}
Look
  • 11
  • 2
0

You can try this, I got a 100%:

   public int[] solution(int[] A, int K) {

        if (A.length == 0) {
            return A;
        }

        for (int i=0;i<K;i++) {

            int[] aux = new int[A.length];
            aux[0] = A[A.length-1];
            System.arraycopy(A, 0, aux, 1, A.length - 1);
            A = aux;
        }

        return A;
    }
0

The time complexity of this would be really less :)
I tried a different approach I believe, without loops:

https://app.codility.com/demo/results/trainingE33ZRF-KGU/

public static int[] rotate(int[] A, int K){
    if ( K > A.length && A.length > 0)
        K = K % A.length;
    if (K == A.length || K == 0 || A.length == 0){
        return A;
    }
    int[] second = Arrays.copyOfRange(A, 0, A.length - (K));
    int[] first = Arrays.copyOfRange(A, A.length - (K), A.length );
    int[] both = Arrays.copyOf(first, first.length + second.length);
    System.arraycopy(second, 0, both, first.length, second.length);
    return both;
}
סטנלי גרונן
  • 2,917
  • 23
  • 46
  • 68
0

Each element is shifted right by one index, and the last element of the array is also moved to the first place.

public int[] solution(int[] A, int K) {
 if (K > 0 && A.length > 0) {
  K = K % A.length;
  int[] B = new int[A.length];
  for (int i = 0; i < A.length; i++) {
   if ((i + K) > (A.length - 1)) {
    B[i + K - A.length] = A[i];
   } else {
    B[i + K] = A[i];
   }
  }
  return B;
 } else {
  return A;
 }
}
Kathees
  • 124
  • 6
  • 13