为什么ArrayList add()和add(int index,E)复杂度是分摊的常量时间?
为什么不对单个add()操作使用O(1),对单个add(int index,E)操作使用O(n),使用任何(任意)add添加n个元素(n add operations)使用O(n)方法?假设我们使用add(int index,E)很少添加到数组端?
阵列(和ArrayList)的一个操作复杂性已经有n个元素:
如果我们进行一百万次插入,1和2的平均值不能为O(1),对吧?
为什么Oracle说
添加操作以分摊的常量时间运行,即添加n 元素需要O(n)时间。
我认为add()的复杂度为O(1),add(int index,E)的复杂度为O(n)。
这是否意味着" n次操作的整体复杂性" (复杂度O(1)的每个运算)可以说是n * O(1)= O(n)。我错过了什么?
也许对于Oracle"添加操作"总是意味着只有add()?并且添加(int,E)是"插入操作"?然后完全清楚!
我猜测,它与渐近分析和摊销分析之间的差异有关,但我无法把握到最后。
What is amortized analysis of algorithms?
Why the time complexity of an array insertion is O(n) and not O(n+1)?
More appropriate to say Amortized O(1) vs O(n) for insertion into unsorted dynamic array?(不太清楚)
答案 0 :(得分:13)
用Oracle术语(暗示默示)和谈论List
boolean add(E)
boolean add(int index, E)
当Oracle写
时添加操作以分摊的常量时间运行,即添加n 元素需要O(n)时间。
意思是:
单个boolean add(E)
操作的复杂性按摊销O(1)。
它不能只是O(1)渐近(总是)因为我们很少需要增加数组容量。这个单独的添加操作实际上是“创建新的更大的数组,将旧的数组复制到其中,然后将一个元素添加到结尾”操作是O(n)渐近复杂度,因为在增加List容量时复制数组是O( n),增长加上加法的复杂度是O(n)[计算为O(n)+ O(1)= O(n)]。如果没有这种容量增长操作,增加的复杂性就是O(1),元素总是被添加(追加)到数组的末尾(最大索引)。如果我们“添加”(=插入)不到数组结束,我们需要移动最右边的元素(具有更大的索引),并且单个这样的操作的复杂性将是O(n)。
现在,对于单个添加操作,渐近复杂度是O(1)用于无增加容量,O(n)用于增加容量增加(这种情况非常罕见)。
单个添加操作的分摊复杂性为O(1)。它反映了这样一个事实,即罕见的O(n)生长和添加操作被更多的O(1)无添加 - 无增长“稀释”,因此“平均”单个操作是O(1)。 / p>
n个加法运算的“渐近复杂度”是O(n)。但在这里我们谈论n个操作的复杂性,而不是一个操作的复杂性。这样做并不是一种严格的方式(“渐近复杂度”),但无论如何。 n次操作的摊销复杂性更不明显。
最后,boolean add(int index, E)
单一操作复杂度始终为O(n)。如果触发增长,则为O(n)+ O(n)[grow + insert],但2 * O(n)与O(n)相同。