【问题标题】:std::list or std::multimapstd::list 或 std::multimap
【发布时间】:2011-02-08 00:27:18
【问题描述】:

嘿,我现在有一个我创建的结构列表,每次添加新对象时,我都会使用 std::list 排序方法对这个列表进行排序。 我想知道什么会更快,为此使用 std::multimap 或 std::list, 因为我每帧都在迭代整个列表(我正在制作游戏)。

我想听听你的意见,我应该如何处理这个事件。

【问题讨论】:

  • 列表是万不得已的容器——我对这个主题的看法见punchlet.wordpress.com/2009/12/27/letter-the-fourth
  • 您还与这些对象有什么关系?每次都迭代它?提取分钟?已经是瓶颈了吗?名单有多大?
  • 当您想要查找列表中的项目时,您是如何查找它们的?
  • 这很重要,它会影响我的完整渲染循环,我会迭代整个列表,而且它非常大(可能每个 9999,但在极端情况下确实如此)。
  • 9999 项并没有那么多。除非物品很大。考虑 x686 使用 4096 字节页面的事实,这意味着您最坏的情况适合 3 个页面。一点也不多。该数据集足够小,在某些情况下甚至可以放入 CPU 的 L2 缓存中。

标签: c++ list sorting stl iteration


【解决方案1】:

std::multimap 可能会更快,因为每次插入需要 O(log n),而列表的插入和排序需要 O(n log n)。

根据您的使用模式,您最好使用排序的vectors。如果你一次插入一大堆项目,然后进行一堆读取——即读取和写入不交错——那么vectorstd::sortstd::binary_search 的性能会更好。

【讨论】:

  • +1 引用vector(你也可以说deque,这可能会更好),但是我也建议在这里multiset,因为它似乎不是关键/值对。
  • 即使您使用带有 std::sort 的 std::vector 插入多个项目也不会给您比 std:set 或 std::multimap 更好的性能,因为排序总是有 O(n log n ) (列表中的 n 项)并且将 k 项插入到具有 n 项的集合中将有 O(k log n),如果 k 远小于 n 则更好。
  • @MKroehnert:这就是我谈论非交错读写的原因。也就是说,与 N 相比,K 会很重要。还要记住,与地图相比,vector s 具有更好的参考位置以及空间效率。所有必须在地图中进行的额外取消引用在理论上的复杂性中都没有考虑,但它肯定会对实际代码产生影响。请参阅 Scott Meyers 的 Effective STL Item 23:考虑用排序的向量替换关联容器。
【解决方案2】:

您可以考虑使用 lower_bound 算法来查找插入列表的位置。 http://stdcxx.apache.org/doc/stdlibref/lower-bound.html

编辑:根据 Neil 的评论,请注意这适用于任何序列容器(向量、双端队列等)

【讨论】:

  • 请注意,由于线性搜索时间,lower_bound 在列表上的表现会很差。否则 +1。
  • @Billy ONeal:不,检查参考。它是 O(log n)。大概使用二分搜索。
  • 只有 (log n) 带有随机访问迭代器。 List 仅提供双向迭代器,使其成为线性的。这将是比较次数的对数,但您仍然需要线性横向遍历列表。
【解决方案3】:

如果您不需要键/值对,std::setstd::multiset 可能比使用 std::multimap 更好。

参考std::sethttp://www.cplusplus.com/reference/stl/set/

参考std::multisethttp://www.cplusplus.com/reference/stl/multiset/

编辑:(之前好像不清楚) 一般来说,使用std::(multi)setstd:(multi)map 之类的容器比使用std::list 并在每次插入元素后对其进行排序要好,因为std::list 在容器中间插入元素时表现不佳。

【讨论】:

  • 注意,对于list来说,一个一个地插入,即使是线性插入时间,至少也是O(n^2)。最好将它们全部插入并排序一次。
  • 这不是真的,因为你会插入 n 次 O(log n),所以你会得到 O(n log n),这与排序相同。
  • @MKroehnert - 列表不允许对数插入。
  • 问题是你只有O(log n) 插入到一个随机可访问的结构中。在列表中,您只有双向遍历,因此 O(n) 插入即使它已排序...
  • 我不建议使用 std::list。事实上,我试图陈述相反的观点。
【解决方案4】:

一般来说,迭代一个容器所花费的时间可能与迭代另一个容器所花费的时间一样多,所以如果你不断地添加一个容器然后对其进行迭代,那么主要是选择一个容器来避免不断地不得不重新分配内存并以您想要的方式快速插入。

list 和 multimap 都将避免仅仅通过添加元素来重新分配自己(就像您可以使用矢量一样),因此主要是插入需要多长时间的问题。添加到列表的末尾将是 O(1),而添加到多重映射将是 O(log n)。但是,multimap 将按排序顺序插入元素,而如果要对列表进行排序,则必须以 O(n log n) 对列表进行排序或以排序方式插入元素类似lower_bound 的东西是O(n)。无论哪种情况,使用列表都会更糟糕(至少在最坏的情况下)。

一般来说,如果您要按排序顺序维护容器并不断向其中添加容器,而不是创建容器并对其进行一次排序,那么集合和地图的效率会更高,因为它们旨在进行排序。当然,与往常一样,如果您真的关心性能,那么分析您的特定应用程序并查看哪个效果更好是您需要做的。但是,在这种情况下,我想说这几乎可以保证 multimap 会更快(尤其是当您有很多元素时)。

【讨论】:

  • 从算法上讲,一般来说,迭代一个容器的时间比另一个容器长。在实践中,由于缓存的考虑,类数组结构(vectordeque)效率更高。
猜你喜欢
  • 2023-03-05
  • 2023-03-14
  • 2013-02-27
  • 1970-01-01
  • 2011-01-19
  • 1970-01-01
  • 2011-02-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多