Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/313.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 阵列的循环旋转解释_Python_Arrays_Algorithm_List - Fatal编程技术网

Python 阵列的循环旋转解释

Python 阵列的循环旋转解释,python,arrays,algorithm,list,Python,Arrays,Algorithm,List,前提:我的问题不是重复的。我不是问如何解决问题,也不是问为什么我的解决方案不起作用,我已经解决了问题,而且有效。我的问题是关于我发现的同一问题的另一个特定解决方案,因为我想了解另一个解决方案背后的逻辑 我遇到了以下循环数组旋转问题(源代码下方): 给出了一个由N个整数组成的数组A。数组的旋转意味着每个元素右移一个索引,数组的最后一个元素移到第一位。例如,数组A=[3,8,9,7,6]的旋转为[6,3,8,9,7](元素向右移动一个索引,6移动到第一位)。 目标是将数组旋转K次;也就是说,

前提:我的问题不是重复的。我不是问如何解决问题,也不是问为什么我的解决方案不起作用,我已经解决了问题,而且有效。我的问题是关于我发现的同一问题的另一个特定解决方案,因为我想了解另一个解决方案背后的逻辑

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

给出了一个由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
有人能解释一下这个特殊的解决方案是如何工作的吗?(作者未在其网站上解释)

对于大型
A
K
,我的解决方案性能不太好,其中
K
,例如:

    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
,我的代码的时间复杂度为
O(N*K)
,其中N是数组的长度。 对于大的
K
和小的
N
K>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)

这一解决方案背后的逻辑是什么


感谢您的关注。

让我先谈谈基本情况下的
K
,这种情况下的想法是将数组分成两部分
A
B
A
是第一个N-K元素数组,
B
是最后的K个元素。算法分别反转
A
B
,最后反转整个数组(两部分分别反转)。要管理使用
K>N
的情况,请考虑每次反转数组N次,您将再次获得原始数组,这样我们就可以使用模块运算符找到拆分数组的位置(只反转真正有用的时间,避免无用的移位)

图形示例

一个图形化的分步示例有助于更好地理解该概念。注意

  • 粗体线表示数组的拆分点(
    K=3
    ,在本例中)
  • 红色数组表示输入和预期输出
从:

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

请注意,我们想要在最终输出之前反转最后3个字母,现在让我们将其反转到位(算法的第一个反转):

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

我们已经有了解决方案,但在相反的方向上,我们可以反向求解整个阵列(算法的第三个也是最后一个反向):

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

代码示例

让我们再给出另一个python代码的分步示例,从以下内容开始:

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)

这是一个非常简单的Ruby解决方案。(可变性得分为100%) 删除数组中的最后一个元素,并将其插入开头

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

你真的在问为什么
a,b=b,a
交换
a
b
?不,我不知道。我了解它的功能,我不了解3
reverse()
调用以及其中索引的相关计算。中有一些关于算法的信息。顺便问一下,说这个循环旋转算法的总体时间复杂度是
O(N)
,对吗,其中,
N
是列表中的元素数,因为假设
K%N
,我们有
O(K/2)
用于右块的第一次反转操作,
O(N-K/2)
用于左块的左反转操作,O(N/2)用于反转整个列表,这导致
O(K/2+N-K/2+N/2)=O((K+N-K+N)/2)=O(2*N/2)=O(N)
,对吗?不完全是正式的,如果你用时间复杂度术语说话,你不能说
O(K/2)
,因为问题的实例是
N
K
只是一个常数项。所以最坏的反转情况是
0(N)
,重复3次就会得出
O(N)
。如果您谈论有效的交换数量(纯交换,我们现在谈论的是
O()
),在这种情况下,我们可以说算法进行了
K/2+N-K/2+N/2=N
交换。注意,多次分析交换次数对于给出算法的效率和想法非常重要。好的。但是:
最坏的反转情况是O(N),重复3次会再次给出O(N)
这不意味着
O吗(N) 
重复3次应导致
O(3*N)
?这不适用于此算法,该算法具有
N
交换数,其中
N
也是列表/数组的元素数。此外,我想说反向函数的时间复杂度始终为
O(N/2)
,因为元素是通过循环交换的,直到到达数组的中间…我只是尝试按照这个推理,以便更好地理解。您以错误的方式使用渐近符号。请查看属性