可解析的阵列和摊销运行时
Resizable Array and Amortized Runtime
"因此,插入n元素的工作总计为o(n)工作。每个插入平均为o(1),每个插入在最坏情况下需要O(n)时间。"这句话是在破解编码访谈时发现的。我有点理解这一说法,即使对此有一小件事让我感到愤怒。在好日子里,摊销的插入为o(1)。这仅意味着当可解析的数组不需要调整大小时,插入某些内容就是o(1)。这很明显。但是,在糟糕的一天,当我们用完太空时,我们需要o(n)插入额外的元素。但是,在最坏情况下,我不同意上面的说法。不应该说,一个插入在最坏的情况下以o(n)为例。
要使这更清楚,这是我在说什么的示例。
说我们有一个可重大的数组,但是我们的数组是尺寸4。我们现在说要插入5个元素,我们将有o(1),o(1),o(1),o(1),o(1),但是然后,一旦到达了最后一个元素,我们就必须将所有这些家伙复制到一个新数组中,并且此过程将使我们的成本为O(n)。
有人可以为我澄清一下。我不明白为什么这本书说当我们只需要将所有元素复制到旧数组中的空间时,只需要将所有元素复制到一个新数组时,就会说某些情况会采用o(n)。
请考虑可重新分配数组中n个插入的成本(我将在此处使用tilde表示法):
- n插入的成本=新元素插入的成本 lize的成本
新元素插入的成本
这只是将新元素插入数组中的成本乘以您插入新元素的次数,即n:
- 新元素插入的成本= 1 * n
rative的成本
想象您有一个64个单元格数组。然后,这意味着该数组已经调整大小:
- 数组大小= 1->2 - >4->8->16->32->64
- #Resize完成= 6
64个单元格数组的调整大小6次,即对log2(64)次进行调整大小。通常,现在我们知道,对于n个插入,我们将执行log2(n)调整大小操作。
但是我们在每个调整大小中该怎么办?我们将复制新的调整大小数组中的数组中已经存在的元素:at resize" i i",我们将复制多少个元素?2^i。与以前的示例:
- 调整数字1 = 1->2:复制1个元素
- 调整数字2 = 2->4:复制2个元素
- 调整数字3 = 4->8:复制4个元素
- ......
- 调整数字6 = 32->64:复制32个元素
so:
- ressize = ummhatory的成本(从i = 1到log2(n))2^i = 2(n-1)
结论
- n插入的成本 =新元素插入的成本 lastize的成本= n 2(n-1)〜 3n
我认为您最好像以这种方式理解上述语句。
首先。数组大小仅为1。并将其插入一个元素。现在阵列已满!您必须调整上一个前一个的大小2次。
接下来,数组大小为2。让此过程进展。您可以轻松注意到必须调整大小的阵列的那一刻为1、2、4、8、16、32,...,2^r。
我会给你问题。
- 您必须调整数组大小的瞬间几次?
- 直到n(n> = 0)步骤的总成本多少?
第一个答案是地板(LGN)时间。我认为您可以很容易地弄清楚。如果您找到第一个答案,请计算第二个步骤的总成本,这是第二个答案很容易。(我不知道我如何表达数学符号:<)
1 2 4 4 8 16 ... 2^(地板(lgn))= 2^(地板(lgn) 1)-1 => o(n)
要获得每个步骤的平均成本,将总成本分为n => o(1)
我认为最坏的情况是,参考文献是在需要调整阵列时。此调整的成本与数组中的元素数量成正比,o(n)
让我们将所有插入插入"重"插入中,这些插入与元素数量和"光"插入成正比的插入,这些插入仅需花费恒定的时间才能完成。然后,如果您从一个空列表开始并保持附加和添加,那么您将主要插入轻度插入,但是时不时会进行大量的插入。
假设,为简单起见,您每次耗尽空间时都会使数组的大小加倍,并且从大小4的数组开始,然后第一个调整大小必须移动四个元素,第二个元素,第二个元素移动八个,然后移动16个,然后移动32个,然后移动64,然后是128,然后256等。
请注意,不仅需要一个单一的附加量需要很长时间 - 如果您进行了n个总插入,那么它们的大约记录n将很重(它们之间的时间不断增长),而另一个大约是n-它们的日志n将很轻。
std::vector
将使所有元素彼此彼此保持在记忆中以进行快速迭代 - 这就是它的事这就是为什么每个人都喜欢std::vector
的原因。通常,它将保留比其中包含的元素数量的空间要多,或者内存恰好是免费的,因此,当您在vector
的末尾添加新元素时,vector
很快就将您的新元素推入那里是很快的。
但是,当vector
没有扩展空间时,它不能仅仅将现有元素留在其位置,并在其他地方启动新列表 - 所有元素都必须在内存中彼此相邻!因此,它必须找到一块免费的内存,该内存足够大,适合所有元素加上您的新元素,然后在那儿复制所有现有元素,然后将您的新元素添加到末尾。
如果添加1个元素需要1个单位的时间单位,则通常需要n个单位来移动n个元素,从广义上讲。如果添加一个新元素,那是一个操作。如果您添加了新元素和1024个现有元素需要重新定位,那就是1025个操作。因此,重新分配所需的时间与向量的大小成正比,因此O(N)
。
- CMake-按正确顺序将项目与C运行时对象文件链接
- 我在c++代码中生成了一个运行时#3异常
- 为什么在运行时没有向我们提供有关分段错误的更多信息?
- 删除指向指针的指针是运行时错误吗
- 如何用参数值调用函数(仅在运行时已知)
- 为什么即使使用-cudart-static进行编译,库用户仍然需要链接到cuda运行时
- 是否可以在编译时初始化数组,以便在运行时不会花费时间?
- c++中的指针和运行时错误
- 在运行时处理类型擦除的数据-如何不重新发明轮子
- 有没有一种方法可以测量c++程序的运行时内存使用情况
- 建议在运行时将带有类实例的列表从c++导入qml
- 无法理解此 return 语句的功能,没有它就会发生运行时错误
- 如何在GTK程序运行时禁用屏幕保护程序/电源管理/屏幕消隐
- 在同一模拟中使用静脉和静脉_ inet内容时出现运行时错误
- 读取文件时运行时的未知行为
- 函数在Windows或Linux上运行时表现不同
- 在声明中合并两个常量"std::set"(不是在运行时)
- AWS Lambda C++运行时权限被拒绝
- 获得0作为分配的阵列的大小,其大小在运行时给出
- 可解析的阵列和摊销运行时