我有algorithms适用于动态增长的列表(连续的内存,如C ++向量,Java ArrayList或C#List)。直到最近,这些算法才会在列表中间插入新值。当然,这通常是一个非常缓慢的操作。每次添加项目时,需要将其后的所有项目转移到更高的索引。对每个算法执行此操作几次,事情真的慢。
我的意识是我可以将新项添加到列表的末尾,然后将它们旋转到位。这是一个选择!
另一种选择,当我知道我提前添加了多少项时,就是在后面添加许多项目,移动现有项目,然后在我为之创建的洞中就地执行算法我。否定的是,我必须在列表的末尾添加一些默认值,然后才覆盖它们。
我对这些选项进行了快速分析,并得出结论,第二种选择更有效。我的理由是,使用第一个选项的轮换将导致就地交换(需要临时)。我唯一担心的第二个选项是我创建了一堆默认值,这些默认值会被丢弃。大多数情况下,这些默认值将为null或填充内存的值类型。
但是,我希望有其他熟悉算法的人告诉我哪种方法会更快。或者,也许我没有考虑过更有效的解决方案。
答案 0 :(得分:2)
对于除阵列末尾之外的任何位置的大量插入或删除,数组都不是有效的。考虑使用不同的数据结构(例如在其他答案之一中建议的数据结构)是否可能更有效。在不知道您试图解决的问题的情况下,几乎不可能建议数据结构(对于所有问题都没有解决方案)。那就是说......
第二种选择绝对是两者中更好的选择。一个更好的选择(避免默认值问题):只需将789
复制到最后并用789
覆盖中间456
。所以唯一的中间步骤是0123789789
。
然而,您的默认值问题(通常)并不是一个大问题:
在Java中,对于其中一个,你不能(据我所知)为一个不是0或空填充的数组分配内存。 C ++ STL容器也强制执行这个我相信(但不是C ++本身)。
与任何中等大小的类相比,指针的大小是最小的(因此将其分配给默认值也花费最少的时间)(在Java和C#中,一切都是指针,在C ++中你可以使用指针(某些东西)像boost::shared_ptr
或指针向量优于直指针(N / A到基元,它们很小,所以通常也不是一个大问题)。
我还建议在开始插入数组末尾之前强制重新分配到指定的大小(Java ArrayList::ensureCapacity
或C ++' {{1 }})。如果你不知道 - 变长长度阵列的实现往往有一个比vector::reserve
返回的内部数组大或可访问的内部数组(为了防止不断重新分配)插入或删除值时的内存。)
另请注意,复制数组部分的方法比使用for循环手动更有效(例如Java size()
)。
答案 1 :(得分:1)
您可能需要考虑将列表的表示形式从使用动态数组更改为使用其他结构。以下两个选项可让您有效地实施这些操作:
订单统计树是一种修改后的二叉树类型,它支持在O(log n)时间内的任何位置插入和选择,以及在O(log n)时间内查找。由于指针和额外簿记的开销,这会大大增加内存使用量,但应大大加快插入速度。但是,它会减慢查找速度。
如果您总是事先知道插入点,则可以考虑切换到链接列表而不是数组,只需保留指向将发生插入的链接列表单元格的指针。但是,这会减慢对O(n)的随机访问速度,这可能是您设置中的问题。
或者,如果您总是知道插入将在何处发生,您可以考虑将数组表示为两个堆栈 - 一个堆栈将数组内容保存在插入点的左侧,另一个堆栈保存(反向)插入点右侧的元素。这使得插入速度很快,如果你有合适的堆栈实现,可以快速保持随机访问。
希望这有帮助!
答案 2 :(得分:0)
HashMaps和链接列表是针对您遇到的问题而设计的。给定带有编号项的索引数据结构,在中间插入项目的难度需要重新编号列表中的每个项目。
您需要一个经过优化的数据结构,以使插入具有恒定的O(1)
复杂度。 HashMaps旨在使插入和删除操作快速闪烁,无论数据集大小如何。
我不能通过描述来假装HashMap主题正义。这是一个很好的介绍:http://en.wikipedia.org/wiki/Hash_table