tldr 未排序的数组 类似于集合。像集合一样,元素可以添加和删除、迭代和读取。但是,与集合一样,谈论在特定位置插入元素是没有意义的,因为这样做会试图将排序顺序强加于定义上未排序的元素。
根据学术文献,数组是常数 O(1),而链表是线性 O(n)。
值得理解为什么学术文献引用数组插入为 O(1) 的数组。有几个概念需要理解:
-
数组被定义为未排序的(除非另有明确说明)。
-
数组的长度,定义为数组包含的元素个数,可以在O(1)时间内任意增加或减少,并且对数组的最大大小没有限制数组。
(在真实的计算机中并非如此,由于内存大小、虚拟内存、交换空间等各种因素。但对于算法asymptotic analysis,这些因素并不重要——我们关心随着输入大小向无穷大增加,算法的运行时间如何变化,而不是它在具有特定内存大小和操作系统的特定计算机上如何执行。)
-
Insert 和 delete 是 O(1),因为数组是未排序的数据结构。
-
插入不是赋值
考虑将元素添加到未排序的数据结构的实际含义。由于没有定义排序顺序,因此实际发生的任何顺序都是任意的并且无关紧要。如果您从面向对象的 API 角度考虑,方法签名将类似于:
Array.insert(Element e)
请注意,这与其他数据结构的插入方法相同,例如链表或排序数组:
LinkedList.insert(Element e)
SortedArray.insert(Element e)
在所有这些情况下,insert 方法的调用者并没有指定插入的值最终存储在哪里——它是数据结构的内部细节。此外,调用者尝试在数据结构中的特定位置插入元素是没有意义的——对于已排序或未排序的数据结构。对于(未排序的)链表,根据定义,该列表是未排序的,因此排序顺序无关紧要。对于已排序的数组,根据定义,插入操作将在数组的特定点插入一个元素。
因此,将数组插入操作定义为:
Array.insert(Element e, Index p)
使用这样的定义,调用者将覆盖数据结构的内部属性并对未排序的数组施加排序约束——该约束在数组的定义中不存在,因为数组是未排序的。
为什么这种误解发生在数组而不是其他数据结构上?可能是因为程序员习惯于使用赋值运算符来处理数组:
array[0] = 10
array[1] = 20
赋值运算符给数组的值一个明确的顺序。这里需要注意的重要一点是 assignment 与 insert 不同:
-
插入 :将给定值存储在数据结构中,而不修改现有元素。
-
insert in unsorted :将给定的值存储在数据结构中,而不修改现有元素,检索顺序并不重要。
-
insert in sorted :将给定值存储在数据结构中,而不修改现有元素,检索顺序很重要。
-
assign a[x] = v :用给定的值 v 覆盖位置 x 中的现有数据。
未排序的数组没有排序顺序,因此插入不需要允许覆盖位置。 insert 与 assignment 不同。数组 insert 简单定义为:
Array.insert(v):
array.length = array.length + 1
// in standard algorithmic notation, arrays are defined from 1..n not 0..n-1
array[array.length] = v
这是O(1)。