(这个问题的灵感来自于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
)插入到线性时间内连续位置的动态数组中?
答案 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)
。如果要插入的元素数量大约为n
(k=O(n)
),那么您将获得O(n+n)=O(2n)=O(n)
因此线性时间。