【问题标题】:merge vector into existing vector将向量合并到现有向量中
【发布时间】:2012-08-28 09:32:47
【问题描述】:

在 C++ 中,给定 vector<T> src, dst,两者都已经排序,是否有一种更有效的方法来将 src 的内容合并到 dst 中而不是

size_t n = dst.size();
dst.insert(dst.end(), src.begin(), src.end());
std::inplace_merge(dst.begin(), dst.begin() + n, dst.end());

?在我关心的情况下,T 是一个很小的(12-16 字节,取决于 ABI)POD 结构,但每个向量包含数百万个元素,因此所使用的内存总量为数十到数百兆字节。

【问题讨论】:

  • 我认为inplace_merge 的最后一个参数需要是dst.end() 而不是dst.size()
  • 马克:你说得对,我的源文件中正确,但在编写问题时以某种方式破坏了它。现已修复。
  • @yes123:这个问题不是关于合并排序的向量。

标签: c++ optimization stl


【解决方案1】:

如果复制繁重的 T 并且您的编译器支持 C++0x,则可以更有效地完成。

#include <iterator> // for make_move_iterator

size_t n = dst.size();

dst.insert(dst.end(),
    std::make_move_iterator(src.begin()),
    std::make_move_iterator(src.end()));

std::inplace_merge(dst.begin(), dst.begin() + n, dst.end());

使用make_move_iterator() 将导致insert()src 的内容移动到dst,而不是复制它们。

更新:

您正在处理 POD 类型,并且您已经在调整/复制 dst 向量中的所有内容,以防 insert() 溢出保留区,因此将 std::merge() 用于新的vector。这将避免初始副本并具有更好的最坏情况复杂性:

inplace_merge() 具有 O(n) 复杂度的最佳情况,但降级为最坏情况 O(n log n ) 取决于您的数据。

merge() 有一个最坏情况的 O(n),因此可以保证至少同样快,甚至可能快得多。它还内置了移动优化。

【讨论】:

  • 最后一行应该是dst.end(),你大概可以把n设为const。
  • 如果 T 复制起来很繁重,但您的编译器不支持移动语义,您可以调整 dst 的大小,然后将 src 的每个元素交换到位。显然 T 需要一个优化的交换。而且我从未使用过inplace_merge,但它看起来好像做了很多分配和/或复制,在没有移动的情况下无论如何这可能会影响性能。
  • 在我关心的情况下,T 是一个小的(12 字节,可能在 vector 中填充到 16)POD 结构,所以在我看来,复制和移动都会退化为 @ 987654336@.
  • 是的。在这种情况下,您仍然可以在另一个答案中尝试 merge 建议。他没有说出来,但 merge 实际上可以更有效,具体取决于您的数据:inplace_merge 在最好的情况下是 O(n),在最坏的情况下是 O(n log n)。 merge 总是 O(n)。
  • 向量可能有数百万个元素,我不认为将所有内容复制到新向量中是个好主意。
【解决方案2】:

我至少会尝试:

std::vector<T> tmp;
tmp.reserve(src.size() + dst.size()); // commenters are probably right about this
std::merge(src.begin(), src.end(), dst.begin(), dst.end(), std::back_inserter(tmp));
src.swap(tmp);

但我怀疑这很大程度上取决于T 的性质、srcdst 的大小,以及我们需要优化的原因。

【讨论】:

  • 为了避免tmpback_inserter 一起增长,为什么不将其构造为std::vector&lt;T&gt; tmp(src.size() + dst.size())?还是使用reserve
  • @Oli,我宁愿使用reserve,以防默认构造很昂贵。
  • 那会构造很多元素——为什么不只是reserve合并前的向量呢?
  • 当然,.reserve() 在这里是个好主意...相应地进行了编辑。
  • 如果T 是一个讨厌的类型,复制成本很高,那么您可以解压缩merge 并将其更改为默认构造,然后从原始结构中交换,而不是复制。 C++0x 有一个push_back(T &amp;&amp;),但没有帮助就不会使用它,因为被推送的东西不一定是一次性的,所以将此答案与 Cory 的结合起来。
【解决方案3】:

如果您的元素的默认初始化比复制便宜很多,您可以取消insert 调用并调整目标向量的大小。然后实现你自己的合并,倒退 - 将迭代器保持在源端和目标的旧端,并移动或复制到目标的新端。当你到达源的开头时,你就完成了。

【讨论】:

    猜你喜欢
    • 2016-05-19
    • 1970-01-01
    • 1970-01-01
    • 2015-11-27
    • 1970-01-01
    • 2018-07-29
    • 2018-09-01
    • 1970-01-01
    • 2015-08-27
    相关资源
    最近更新 更多