Arrays 为什么插入在动态数组O(1)的结尾,而插入中间的是O(n)?

Arrays 为什么插入在动态数组O(1)的结尾,而插入中间的是O(n)?,arrays,data-structures,big-o,Arrays,Data Structures,Big O,根据上的Wikipedia文章,在数组末尾插入/删除是O(1),而从中间插入/删除是O(n)。这到底是为什么 另外-如果我有一个包含5个元素的动态数组,并且我在位置6插入了一个新元素,那么操作是O(n),而如果我使用函数附加到数组的末尾,则操作是O(1)。假设阵列在这两种情况下都不需要调整大小,这不是相同的操作吗?或者附加到动态数组真的会将新元素插入位置6之外的某个位置吗 谢谢 编辑:我想我的主要困惑是在数组末尾插入和在与数组末尾相同的特定位置插入之间的区别 我假设指向数组末尾的内存地址的指针很

根据上的Wikipedia文章,在数组末尾插入/删除是O(1),而从中间插入/删除是O(n)。这到底是为什么

另外-如果我有一个包含5个元素的动态数组,并且我在位置6插入了一个新元素,那么操作是O(n),而如果我使用函数附加到数组的末尾,则操作是O(1)。假设阵列在这两种情况下都不需要调整大小,这不是相同的操作吗?或者附加到动态数组真的会将新元素插入位置6之外的某个位置吗

谢谢

编辑:我想我的主要困惑是在数组末尾插入和在与数组末尾相同的特定位置插入之间的区别


我假设指向数组末尾的内存地址的指针很方便,这就是为什么追加操作很快的原因。相反,如果我指定了一个精确的位置(即使它是数组的末尾),它也不会知道在该位置插入等同于使用前面提到的内存地址,因此它必须遍历整个数组,呃?

要在数组的末尾插入,只需将项放在那里

要插入到数组的中间,需要将该点之后的项向上移动1

要从数组末尾删除,只需将其计数减1即可

要从中间删除,您必须这样做,并将其他项目向下移动

正是移位把它变成了O(n)。

很简单:

在中间插入包括将每个后面的元素移动1。


要在末尾插入,如果保留了额外的空间,则项目仅存储在那里,如果没有,则分配新的空间。因此,这个操作是在中完成的。

数量级完全取决于“动态数组”实际是什么样的数据结构(“动态数组”不是一个严格定义的数据结构,它更像是通过使用特定的数据结构实现的期望结果)。您给出的示例是通过使用链表实现的动态数组来反映。如果列表结构保留指向最终元素的指针,则添加到末尾可能是O(1)。插入(不考虑索引)需要遍历链表,这意味着每个节点都要执行一次操作,直到达到所需的索引。

实际上,这不是大O,而是。

为Adam Robinson的优秀总结增添了一点:这不仅仅是理论。我见过许多情况,其中动态数组是通过反复追加到数组末尾来构造的。这将导致o
(N**2)
性能下降,因为数组需要反复重新分配,迫使其每个成员复制到新的数组结构中。重新分配可能只发生在1/10的追加操作上,但这已经够糟糕的了,仍然是o
(N**2)
性能方面的问题


在STL中,可以通过在写入向量之前调用
vector::reserve(N)
来避免这种性能损失;但这一步经常被忽略。

不,这是一个大O。如果位置恰好位于末端,插入位置可能需要恒定的时间。它不受n的限制。我想这就是为什么每次重新分配内存时都会将数组的大小增加一倍的原因。这是应用程序编写人员的典型做法。但是不要假设类库会这样做;许多普通的植物反而以恒定的增量生长。10是我从MFC记得的数字。至于您的编辑,同样取决于实现。你可以很好地创建一个动态数组,它足够聪明,可以知道Insert(bound-1)和Add.Hmm是一样的。我意识到我在这里的一篇文章中问了两个相对不同的问题。亚当·罗宾逊给了一个很好的答案,帕克斯给了另一个更大的答案。对什么应该是公认的答案有任何共识吗???你问的是一个有偏见的群体;)选择一个更有用的答案。如果我们在中间删除一个元素,那么我们可以将它与数组中的最后一个元素交换。这样我们就不必将所有后续元素向左移动一个位置。对于未排序的数组,我们总是可以在O(1)中删除或插入。这很有意义。不幸的是,在我的示例(UniData)中提到的平台上,我不知道动态数组是如何在封面下实现的……但从您所说的,我认为它是一个链表。不知道为什么他们选择了这个而不是数组。许多语言在“动态数组”中使用链表结构。许多还使用使用缓冲区数组声明的实际本机数组。一旦缓冲区填满,它们要么链接一个新数组(创建一种数组/链表混合),要么分配一个具有适当新缓冲区大小的全新数组,然后复制所有现有数据并销毁旧数组。但是,这可能会导致相当多的内存碎片。。。UDT中的大多数变量只是严格意义上的字符串(因为UDT是在“C”中构建的),它们是字节数组。因此,术语“动态数组”是可变的。添加到开始或结束通常更快,因为有指向其位置的指针。添加到中间涉及多个步骤,包括多个内存分配和数据转移。效率不高,就像Java字符串“+”(这就是StringBuilder被发明的原因)。尝试使用维度数组或CALLC。我想不出任何语言使用链表来实现动态数组。动态数组确实没有明确的实现,尽管它们几乎总是意味着它是一个动态大小的数组或可扩展的数组。如果有人把链表称为动态数组,我会说这种说法是错误的。它们没有相同的时间复杂性,并且链表会丢失CPU缓存