给定n个元素的一维数组,以及如何有效地旋转数组,使数组元素向左移动m个位置?是否可以仅使用常数O(1)存储器在O(n)时间复杂度中执行此操作?
例如,如果n = 8且您的数组为[0, 1, 2, 3, 4, 5, 6, 7]
并且您将其向左旋转m = 2,则会得到[2, 3, 4, 5, 6, 7, 0, 1]
。
这是我实现的Python中的天真解决方案,它使用O(n)时间和O(n)内存和临时数组。
def rotateLeft(A, m):
temp = [None]*len(A)
for i in xrange(len(temp)):
temp[i] = A[(i + m) % len(A)]
for i in xrange(len(A)):
A[i] = temp[i]
我怎样才能更有效地做到这一点?我被告知这可以通过恒定的内存量来完成,并且仍处于O(n)时间内。
任何语言的解决方案都可以,任何建议都非常受欢迎。
编辑:我不是在寻找图书馆解决方案。此外,该数组不是链表/双端队列。没有头/尾/下一个/前一个元素的概念。答案 0 :(得分:7)
让我们看一下颠倒最终数组:
[1, 0, 7, 6, 5, 4, 3, 2] (spacing mine)
你看到有趣的东西吗?
答案 1 :(得分:5)
尝试考虑这样的解决方案会是什么样子。如果您不能使用更多空间,唯一可用的移动是交换元素。尝试使用笔,2个元素数组,然后是3个。在得到这个想法后,它应该很容易。
事实上,使用swap需要多一个变量,你可以使用XOR交换算法(http://en.wikipedia.org/wiki/XOR_swap_algorithm)解决这个问题,但我认为这不是很重要。
答案 2 :(得分:1)
由于内存限制,这并非易事。首先将第一个元素移动到它的新位置,因为你不能存储太多元素 - 继续为你刚刚驱逐的元素找到一个位置。
现在想想通过次数与GCD(n,m)的关系,以及算法应该如何反映 - 通过gcd为1的常见情况开始(例如,如果上例中m = 3) - 一旦替换链将结束(您可以通过将当前索引与您开始的索引进行比较来检查),您将完成任务。但是,对于GCD(m,n)> 1,你只会移动部分元素,并且你需要在你开始的最后一个元素后面开始一个新元素链。
现在说服自己,无论阶段数如何,完成的班次总数为O(n)。
答案 3 :(得分:0)
查看以下PseudoCode。对我来说似乎是对的。
function rotate(a[], a.length)
{
i = 0;
j = 0;
k = 0;
temp = a[0];
k = (i + a.length - 2 ) % a.length
while (j < a.length){
temp1 = a[k]
a[k] = temp;
i = k;
temp = temp1
k = (i + a.length - 2 ) % a.length
j++
}
}
答案 4 :(得分:0)
这是@MBo向我暗示的一个常量内存解决方案。除了数组空间之外,它还使用3个额外变量,m:i
,midpoint
和endpoint
。它循环3次,首先反转两个子阵列,然后在最后一个循环中反转整个阵列。它是O(n / 2 + m / 2 +(n-m)/ 2),它只是O(n),因为0 <= m <0。 n由于我在开始时m = m % n
将任何给定的正m减少到数组范围内的索引。
交换还通过2项缓冲区(2个元素的元组)发送值,但它仍然是交换的常量内存,因此无关紧要。
def rotateLeft(A, m):
m %= len(A)
# Reverse A[0..m-1]
midpoint = m/2
endpoint = m-1
for i in xrange(midpoint):
A[i], A[endpoint-i] = A[endpoint-i], A[i]
# Reverse A[m..n-1]
midpoint = m+(len(A)-m)/2
endpoint = len(A)-1
for i in xrange(m, midpoint):
A[i], A[endpoint-(i-m)] = A[endpoint-(i-m)], A[i]
# Reverses all elements of array in place
midpoint = len(A)/2
endpoint = len(A)-1
for i in xrange(midpoint):
A[i], A[endpoint-i] = A[endpoint-i], A[i]
它还允许负旋转(向右旋转),这在我看来非常简洁。这意味着rotateRight
可以通过以下方式实现。
def rotateRight(A, m):
rotateLeft(A, -m)
然后,下面的代码将通过断言检查就好了。
A = [0, 1, 2, 3, 4, 5, 6]
B = A[:] # Make copy of A and assign it to B
rotateLeft(A, 4)
rotateRight(A, 4)
assert(A == B)