在线性时间内将未知数量的元素插入动态数组中

时间:2011-10-24 08:53:52

标签: arrays algorithm complexity-theory dynamic-arrays

(这个问题的灵感来自于deque::insert() at index?,我很惊讶它在我的算法讲座中没有涉及,而且我也没有在其他问题中找到它,甚至没有在维基百科中提到:)。我认为这可能是普遍感兴趣的,我会自己回答......)

动态数组是允许在分摊的常量时间O(1)中添加元素的数据结构(通过每次需要增长时分配的内存大小加倍,请参阅Amortized time of dynamic array进行简短分析)。

但是,在数组中间插入单个元素需要线性时间O(n),因为在最坏的情况下(即在第一个位置插入),所有其他元素都需要移位一个。

如果我想在数组中的特定索引处插入k个元素,那么执行插入操作k次的简单方法会导致O(n*k)的复杂性,如果k=O(n),则为O(n²)的二次复杂度。

如果我提前知道k,解决方案很简单:如果需要(可能重新分配空间)展开数组,将插入点开始的元素移位k并简单地复制新的元件。

但是可能存在这样的情况,我不知道我想提前插入的元素数量:例如,我可能从类似流的接口获取元素,所以我只得到最后一个元素的标志读取。

有没有办法将多个(k)元素(事先不知道k)插入到线性时间内连续位置的动态数组中?

2 个答案:

答案 0 :(得分:4)

您可以简单地分配一个长度 k + n 的新数组,并线性插入所需的元素。

newArr = new T[k + n];

for (int i = 0; i < k + n; i++)
    newArr[i] = i <= insertionIndex      ? oldArr[i]
              : i <= insertionIndex + k  ? toInsert[i - insertionIndex - 1]
              :                            oldArr[i - k];

return newArr;

每次迭代都需要一段时间,它会运行 k + n 次,因此 O(k + n)(或者, O(n)如果你愿意的话。)

答案 1 :(得分:2)

事实上,有一种方法很简单:

  • 首先将所有k元素附加到数组的末尾。由于附加一个元素需要O(1)时间,因此这将在O(k)时间内完成。
  • 第二个将元素旋转到位。如果要在位置index处插入元素。为此,您需要将子阵列A[pos..n-1]向右旋转k个位置(或左侧n-pos-k个位置,这相当)。如Algorithm to rotate an array in linear time中所述,通过使用反向操作可以在线性时间内完成旋转。因此,轮换所需的时间为O(n)

因此算法的总时间为O(k)+O(n)=O(n+k)。如果要插入的元素数量大约为nk=O(n)),那么您将获得O(n+n)=O(2n)=O(n)因此线性时间。