tonix
tonix

Reputation: 6949

Cyclic rotation of array explanation

Premise: My question is not a duplicate of Cyclic rotation in Python . I am not asking how to resolve the problem or why my solution does not work, I have already resolved it and it works. My question is about another particular solution to the same problem I found, because I would like to understand the logic behind the other solution.

I came across the following cyclic array rotation problem (below the sources):

An 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 moved to the first place. For example, the rotation of array A = [3, 8, 9, 7, 6] is [6, 3, 8, 9, 7] (elements are shifted right by one index and 6 is moved to the first place). The goal is to rotate array A K times; that is, each element of A will be shifted to the right K times.

which I managed to solve with the following Python code:

def solution(A , K):
    N = len(A)
    if N < 1 or N == K:
        return A
    K = K % N
    for x in range(K):
        tmp = A[N - 1]
        for i in range(N - 1, 0, -1):
            A[i] = A[i - 1]
        A[0] = tmp
    return A

Then, on the following website https://www.martinkysel.com/codility-cyclicrotation-solution/, I have found the following fancy solution to the same problem:

def reverse(arr, i, j):
    for idx in xrange((j - i + 1) / 2):
        arr[i+idx], arr[j-idx] = arr[j-idx], arr[i+idx]

def solution(A, K):
    l = len(A)
    if l == 0:
        return []

    K = K%l

    reverse(A, l - K, l -1)
    reverse(A, 0, l - K -1)
    reverse(A, 0, l - 1)

    return A

Could someone explain me how this particular solution works? (The author does not explain it on his website)

My solution does not perform quite well for large A and K, where K < N, e.g.:

    A = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] * 1000
    K = 1000
    expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] * 1000
    res = solution(A, K) # 1455.05908203125 ms = almost 1.4 seconds

Because for K < N, my code has a time complexity of O(N * K), where N is the length of the array. For large K and small N (K > N), my solution performs well thanks to the modulo operation K = K % N:

    A = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    K = 999999999999999999999999
    expectedRes = [2, 3, 4, 5, 6, 7, 8, 9, 10, 1]
    res = solution(A, K) # 0.0048828125 ms, because K is torn down to 9 thanks to K = K % N

The other solution, on the other hand, performs greatly in all cases, even when N > K and has a complexity of O(N).

What is the logic behind that solution?

Thank you for the attention.

Upvotes: 2

Views: 4475

Answers (2)

Enow B. Mbi
Enow B. Mbi

Reputation: 319

Here is a very simple solution in Ruby. (scored 100% in codility) Remove the last element in the array, and insert it in the beginning.

def solution(a, k)
  if a.empty?
    return []
  end
  modified = a
  1.upto(k) do 
    last_element = modified.pop
    modified = modified.unshift(last_element)
  end
  return modified
end

Upvotes: 0

FabioL
FabioL

Reputation: 999

Let me talk first the base case with K < N, the idea in this case is to split the array in two parts A and B, A is the first N-K elements array and B the last K elements. the algorithm reverse A and B separately and finally reverse the full array (with the two part reversed separately). To manage the case with K > N, think that every time you reverse the array N times you obtain the original array again so we can just use the module operator to find where to split the array (reversing only the really useful times avoiding useless shifting).

Graphical Example

A graphical step by step example can help understanding better the concept. Note that

  • The bold line indicate the the splitting point of the array (K = 3 in this example);
  • The red array indicate the input and the expected output.

Starting from:

starting array

look that what we want in front of the final output will be the last 3 letter reversed, for now let reverse it in place (first reverse of the algorithm):

first_pass_array

now reverse the first N-K elements (second reverse of the algorithm):

second_pass_array

we already have the solution but in the opposite direction, we can solve it reversing the whole array (third and last reverse of the algorithm):

final_array

Here the final output, the original array cyclical rotated with K = 3.

Code Example

Let give also another step by step example with python code, starting from:

A = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
K = 22
N = len(A)

we find the splitting index:

K = K%N
#2

because, in this case, the first 20 shift will be useless, now we reverse the last K (2) elements of the original array:

reverse(A, N-K, N-1)
# [1, 2, 3, 4, 5, 6, 7, 8, 10, 9]

as you can see 9 and 10 has been shift, now we reverse the first N-K elements:

reverse(A, 0, N-K-1)
# [8, 7, 6, 5, 4, 3, 2, 1, 10, 9]

And, finally, we reverse the full array:

reverse(A, 0, N-1)
# [9, 10, 1, 2, 3, 4, 5, 6, 7, 8]

Note that reversing an array have time complexity O(N).

Upvotes: 8

Related Questions