Delphi TList 中查找和插入的时间复杂度是多少?

What is the time complexity of lookups and inserts in a Delphi TList?

Delphi 中的 "TList" 对象让我对其 "items[i]" 属性 有点困惑。 属性 暗示 TList 可能在内部索引某种数组中的所有列表项。

我完全知道这种索引属性可以在对象内部任意处理(在这种情况下,例如假设通过线性遍历整个链表直到找到具有提供的索引的项目),所以我的问题是:哪个以下备选方案中的一个是正确的:

1。 TList 确实在某种(动态)数组中对其项目进行内部索引,因此具有任意索引的列表项查找将具有 O(1) 时间复杂度(即常量),而列表项添加将具有 O(n) 复杂度(即线性),因为在扩展大小时必须重新分配数组。

2。 TList 不会在某种(动态)数组中对其项目进行内部索引,因此具有任意索引的列表项查找将具有 O(n) 时间复杂度(即线性),而列表项添加将具有 O(1) 复杂度(即常量)。

3。 另一种第三种选择,在这种情况下,非常欢迎您解释查找(按索引)和插入操作时的时间复杂度。

Delphi的TList是一个包裹数组的class。所以它的操作复杂度和数组是一样的。随机访问是 O(1)。对于操作的复制部分,插入是 O(n)。与插入相关的内存分配更难分析并且取决于许多因素。由于 thois,我不确定分析插入的时间复杂度是否真的容易处理。

顺便说一句,从计算机科学的角度来看,Delphi 的列表 classes 不是列表,这一直让我感到沮丧。它们是数组。

购买 Delphi 即可获得 RTL 的源代码,因此很容易看出实际发生了什么。

在幕后,项目在一个动态数组中进行管理,该数组会随着您向其中添加项目而增长,但比每次添加新项目时简单地增长 1 更智能。这意味着后备数组通常会大于 TList 对象中项目的 Count,因此索引是一个 O(1) 操作,加法通常也是 O(1) . (特别是因为 FastMM 内存管理器通常可以在数组增长时就地重新分配数组,尤其是在尺寸较小的时候,所以不需要复制。)但是当支持数组必须增长并被内存管理器复制时,它将是O(n) 操作。

另一方面,Insert 操作(插入到列表的中间,而不是添加到末尾)是一个 O(n) 操作,因为它必须移动插入点向前移动一位。

正如@SirRufo 指出的那样,Capacity 属性 在这里是相关的:它是支持数组的大小。如果 Count = Capacity 并且您尝试 Add 一个新值,那就是它必须调整数组大小的时候。