时间复杂度和插入std::list

Time complexity and insertion into std::list

本文关键字:list std 插入 时间复杂度      更新时间:2023-10-16

在std::list上的插入被声明为常数时间,无论它是否是常数时间在集装箱的前面、中间或后面制造。

另一方面,为新插入的项获取内存由标准分配器,它使用操作符new。AFAIK运营商新不保证要有恒定的时间

当操作符new在堆中查找可用空间时,它必须确保不会覆盖以前分配的内存,因此它必须保持跟踪已经在堆上分配的。我的结论是插入必须至少与列表中已存在的元素个数成线性关系

这个推理有什么错?


我的问题是:

  • 怎么可能说列表上的插入是恒定的呢时间,当获取每个新节点的内存不保证是恒定的时间?

注意:重要的是要注意"真实生活时间"和在深入研究时间复杂性时所讨论的"时间"之间的区别。当讨论时间复杂度时,重要的是不要混淆"time""milliseconds spent doing something"的用法。


常数时间的定义是什么?

在许多情况下,维基百科经常被认为是一个糟糕的参考,但在这种情况下(以及许多其他情况下),可用的定义是正确的,将有助于描述事物的运作方式。

关于时间复杂度的文章说了以下关于常数时间:

Wikipedia - Constant Time

如果T(n)的值被一个不依赖于输入的大小的值所限定,则该算法被称为常数时间(也写为O(1)时间)。例如,访问数组中的任何单个元素需要常量时间,因为只需执行一次操作即可定位该元素。


由于插入到std::list的时间不依赖于列表中元素的个数,所以我们说插入是常数时间;每次插入,无论何时何地,都由相同数量的初等操作组成;与列表大小无关。



但是如果operator new不是O(1)呢?

老实说,这并不重要,即使new的复杂性隐式地依赖于我们分配了多少先前的实体,我们的列表插入的复杂性将保持不变。该分配与列表的大小无关。

O(1)constant time表示在任何给定算法中,执行某项操作的时间与输入的大小无关。即使new不是O(1),我们的插入也是O(1),因为它只描述自己。

列表插入中采用的路径都包含operator new。路径不会因为列表的大小而改变,路径的复杂度是常数时间



我们在处理什么?

##c++ at freenode中的Hannibal_Smith说了一些很聪明的话,我非常喜欢,所以我将它包含在这篇文章中:成本模型是一个指针机。

尽管这句话可能有点误导人,但它确实起到了解释插入是O(1)的目的,尽管部分算法不是常数时间

插入到std::list是从一个只处理指针的机器的角度来描述的,从这个角度来看,我们不能说它就是O(1)。在这个算法内部完成的分配与算法本身的复杂度无关。

这是一个非常棘手的问题,在这个线程中已经讨论了一些长度。

如果我可以试着总结一下:标准做了一些微妙的区别。如果你仔细阅读,有些操作确实被指定为"常数时间",但其中std::list插入是而不是。它被指定为"常量"(编辑:错误,见下文),并且"通用容器要求"条款(本c++标准草案中的23.2.1)解释了

本条款中所有的复杂度要求仅以对所包含对象的操作次数来说明。

(Edit:正如philip rossamen指出的那样,我错了;std::list插入指定为"恒定时间",但我相信一般要求条款仍然适用)。因此,因为列表插入只需要在单个对象及其相邻对象上工作,所以它是"恒定复杂度",尽管它可能不一定是"恒定时间复杂度",因为没有时间复杂度保证分配。

实际上,一个好的内存分配器在分配对象的数量上不会是线性的,尽管它也可能不是常数时间。

相关文章: