Codility CyclicRotation就地实现

时间:2018-07-26 13:27:45

标签: language-agnostic

对于这个问题,我无法解决:

  

给出了一个由N个整数组成的零索引数组A。旋转数组意味着每个元素右移一个索引,并且数组的最后一个元素也移到第一位。

     

例如,数组A的旋转= [3、8、9、7、6]为[6、3、8、9、7]。目标是将阵列旋转K次;也就是说,A的每个元素将向右移动K个索引。

我想创建解决方案而不创建新的数组,而只是修改一个数组。大部分时间都有效。示例测试通过了,其他测试也通过了,但是其中一些Codility未显示输入的测试失败了。

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;
}

我已经跳过了与以下事实有关的代码:您不需要旋转整个数组几次(即,旋转A.Length%K就足够了)。

我的实现有什么问题?我想念一些特殊情况吗?

4 个答案:

答案 0 :(得分:0)

用于此的算法应该非常简单,例如:

aux <- array[array.Length - 1]

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

请注意,在特殊情况下,当array.Length <= 1时,您不需要任何操作即可实现轮换。如果要在不使用数组的情况下实现K旋转,则可以调用K次。

您将需要技巧才能实现最佳算法。在这里,我将采用以下方法。数组的给定元素可以具有三种不同的可能状态。我将通过示例进行解释。如果我将第K个元素放入一个名为aux的变量并将第0个元素放在其位置,那么我们将具有以下三种状态:

  • 在元素0处,原始元素已经移至另一个位置,但是最后一个元素尚未到达。这是移动状态
  • 在元素K处,原始元素已移动,而最后一个元素已到达此处。这是到达状态
  • 在元素2 * K上,到目前为止我们什么都没做,所以我们有原始状态

因此,如果我们可以以某种方式标记元素,则算法将如下所示:

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

现在,我们如何在实践中使用它?让我们考虑当数组仅具有正数作为值的示例。您可以通过为它们分配负值来标记所有元素(例如,-5而不是5)。只要将状态修改为移动,状态值将为0,到达状态时将具有正数。由您决定如何标记这些元素,您将需要根据自己的任务进行标记。如果由于某种原因无法标记元素,则需要创建一个辅助数组来解决此问题。

编辑

不要害怕一会儿,由于模类的原因,它不应该搜索太多步骤。用Java语言实现:

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
    }
}

根据您的意愿更改数组和K的定义。

答案 1 :(得分:0)

由于@dvaergiller发表了与我的方法类似的问题:Fastest algorithm for circle shift N sized array for M position

,我终于设法找出解决方案的问题

这个答案使我意识到,每次A.Length和K的最大公约数都不是1时,我的解决方案都会失败。@ IsaacTurner解决方案更容易理解,并且表明不需要不断切换元素的位置,但是现在我可以更正我的解决方案了。

我基本上不应该遍历数组中的所有元素以为每个元素找到正确的位置,因为如果最大公约数不是1,我将再次开始切换元素。相反,必须在完成整个循环后立即将其停止,然后重新启动以根据下一个位置开始切换。

这是我的解决方案的更正版本:

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;
}

答案 2 :(得分:0)

您可以尝试一下,我得到了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;
    }

答案 3 :(得分:0)

这样的时间复杂度会大大降低:)
我尝试了一种我认为没有循环的不同方法:

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

O_DIRECT