如何使用O(1)辅助空间将数组置换为给定的顺序?

时间:2009-09-27 21:33:28

标签: algorithm permutation shuffle

如何实现以下OrderElements功能?

char chars[] = {'a', 'b', 'c', 'd', 'e'};
int want_order[] = {2, 4, 3, 0, 1};
int length = 5;
OrderElements(chars, want_order, length);

// chars now contains: c, e, d, a, b

当您可以使用线性额外空间时很容易,但是只能使用恒定的额外空间,即直接对chars元素进行原位排序吗?

P.S。:这不是考试题目;我实际上需要这个功能。

澄清:似乎对所需的最终元素顺序存在误解。示例中的结果数组应具有以下元素,引用原始chars数组:

{chars[2], chars[4], chars[3], chars[0], chars[1]}

{'c', 'e', 'd', 'a', 'b'}. 

6 个答案:

答案 0 :(得分:6)

严格来说,需要O(lg length)内存来表示列表索引;但是,我将在本次讨论中忽略这一点,因为使用64位i可能足以支持我们实际可以重新排序的任何内容。

for (int i = 0; i < length; i++) {
  int t = want_order[i];
  while (t < i) {
    // t has been moved already; we need to find out where the value that started there
    // went. Since we must've put it at want_order[t] before, resume looking there
    t = want_order[t];
    // once t >= i, we know we've not touched that slot more than once, and so our
    // value is there
  }
  swap(chars[i], chars[t]);
}

直观的解释:对于数组中的每个项目,我们将目标值放入其中,将旧值存储在包含目标值的插槽中。我们必须注意处理我们的目标价值被取代的情况;通过注意给定的插槽最多只交换两次来处理;一旦当其中的值被另一个值(这个迭代不会发生,因为这个迭代将会这样做)或当值被移位以插入最终值(仅发生在较低的索引)时。

说明样本数据的显示方式:

 i | want_order | chars     | t
 0 |  2 4 3 0 1 | a b c d e | 2 (swap)
 1 |  2 4 3 0 1 | c b a d e | 4 (swap)
 2 |  2 4 3 0 1 | c e d a b | 3 (swap)
 3 |  2 4 3 0 1 | c e d a b | 0 (follow)
 3 |  2 4 3 0 1 | c e d a b | 3 (swap - no-op)
 4 |  2 4 3 0 1 | c e d a b | 1 (follow)
 4 |  2 4 3 0 1 | c e d a b | 4 (swap - no-op)

此算法仅使用O(lg n)内存(对于索引),但我没有尝试完全分析其运行时间。很明显,它是最糟糕的O(n^2),但我怀疑它会比实际情况更好。但是,它可能必须遵循的链的长度没有实际限制,因此原则上它最终可能会使用O(n^2)时间与最坏情况输入。

答案 1 :(得分:1)

不可能。

您至少需要O(log(列表大小))才能知道已排序元素的索引。

答案 2 :(得分:0)

这将在O(n²)中完成。在外部循环的每次迭代中,当前需要的元素被交换到其正确的位置(第一个内部循环),然后调整剩余元素的所需顺序以反映新情况(第二个内部循环)。

for (int i = 0; i < length; i++)
{
    for (int j = wantedOrder[i]; j > i; j--)
    {
        Swap(chars, j, j - 1);
    }

    for (int j = i + 1; j < length; j++)
    {
        if (wantedOrder[j] < wantedOrder[i])
        {
            wantedOrder[j]++;
        }
    }
}

这当然需要销毁所需的订单数组。如果不允许这样做,我不知道如何解决问题(至少在目前)。

答案 3 :(得分:0)

可以这样做如果允许你改变want_order数组。该算法相当简单,因为置换可以分解成循环。当您迭代一个周期时,只需标记每个被访问的周期。时间复杂度为O(N)。伪代码:

char chars[]      = {'a', 'b', 'c', 'd', 'e'};
int  want_order[] = {2, 4, 3, 0, 1};
int  length       = 5;

OrderElements(chars, want_order, length) {
  int i, j, k;

  for(i = 0; i < length; ++i) {
    if (want_order[i] == -1) continue;

    j = startPos = want_order[i];
    c = chars[i];
    do {
      swap(c, chars[j]);
      k = want_order[j];
      want_order[j] = -1;
      j = k
    } while(j != startPos);
  }
}

答案 4 :(得分:0)

上面的帖子有一个错误(它无意中覆盖了一个索引)。这是一个更正版本:

char chars[]      = {'a', 'b', 'c', 'd', 'e'};
int  want_order[] = {2, 4, 3, 0, 1};
int  length       = 5;

OrderElements(chars, want_order, length) {
  int i, j, k;

  for(i = 0; i < length; ++i) {
    if (want_order[i] == -1) continue;

    j = startPos = want_order[i];
    c = chars[i];
    do {
      swap(c, chars[j]);
      k = want_order[j];
      want_order[j] = -1;
      j = k
    } while(j != startPos);
  }
}

答案 5 :(得分:-1)

使用存储器排序操作O(1)是Bubblesort,但它具有性能O(n²)

http://en.wikipedia.org/wiki/Bubble_sort