使用常量存储器旋转m个位置左侧的n个元素的一维数组?

时间:2013-10-08 07:28:01

标签: python arrays algorithm vector

给定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)时间内。

任何语言的解决方案都可以,任何建议都非常受欢迎。

编辑:我不是在寻找图书馆解决方案。此外,该数组不是链表/双端队列。没有头/尾/下一个/前一个元素的概念。

5 个答案:

答案 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:imidpointendpoint。它循环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)