数组解释的循环旋转

时间:2019-09-18 18:46:12

标签: python arrays algorithm list

前提:我的问题不是Cyclic rotation in Python的重复。我不是在问如何解决问题,或者为什么我的解决方案不起作用,我已经解决了它并且起作用了。我的问题是关于发现相同问题的另一种特殊解决方案,因为我想了解另一种解决方案背后的逻辑。

我遇到了以下循环数组旋转问题(在源代码下面):

  

给出由N个整数组成的数组A。旋转数组意味着将每个元素右移一个索引,并将数组的最后一个元素移到第一位。例如,数组A = [3、8、9、7、6]的旋转为[6、3、8、9、7](元素右移一个索引,而6移到第一位)。   目标是将阵列旋转K次;也就是说,A的每个元素将向右移K次。

我设法通过以下Python代码解决了该问题:

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

然后,在以下网站https://www.martinkysel.com/codility-cyclicrotation-solution/上,我找到了以下解决同一问题的方法:

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

有人可以向我解释这个特定解决方案的工作原理吗? (作者未在其网站上解释它)

我的解决方案在大型AK(例如K < N,例如:

    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

由于K < N,我的代码的时间复杂度为O(N * K),其中N是数组的长度。 对于大K和小NK > N),我的解决方案表现出色,这要归功于模运算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

另一方面,即使在N > K且复杂度为O(N)的情况下,另一种解决方案在所有情况下都表现出色。

该解决方案背后的逻辑是什么?

感谢您的关注。

1 个答案:

答案 0 :(得分:3)

首先让我谈谈K < N的基本情况,这种情况下的想法是将数组分为两部分ABA是第一个NK个元素数组和B个最后K个元素。该算法分别反转AB,最后反转整个数组(两部分分别反转)。要处理K > N的情况,请考虑每次N次反转数组就再次获得原始数组,因此我们可以使用模块运算符查找拆分数组的位置(仅反转真正有用的时间,以避免没用的移位)。

图形示例

一个图形化的分步示例可以帮助您更好地理解该概念。请注意

  • 粗线表示数组的分割点(在此示例中为K = 3);
  • 红色数组表示输入和预期输出。

开始于:

starting array

看起来,我们想要在最终输出前面的是倒数的最后3个字母,现在让我们将其倒置到位(算法的第一个倒数):

first_pass_array

现在反转前N-K个元素(算法的第二个反转):

second_pass_array

我们已经有了解决方案,但是在相反的方向上,我们可以通过反转整个数组来解决它(算法的倒数第三次):

final_array

这是最终输出,原始数组以K = 3循环旋转。

代码示例

让我们从pythonp开始,再给出一个逐步的示例,

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

我们找到分裂指数:

K = K%N
#2

因为在这种情况下,前20个移位将无用,所以现在我们反转原始数组的最后K(2)个元素:

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

如您所见9和10发生了移位,现在我们反转前N-K个元素:

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

最后,我们反转整个数组:

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

请注意,反转数组的时间复杂度为O(N)。