【问题标题】:What is the best way to concatenate two vectors?连接两个向量的最佳方法是什么?
【发布时间】:2018-08-20 20:41:15
【问题描述】:

我正在使用多线程并希望合并结果。例如:

std::vector<int> A;
std::vector<int> B;
std::vector<int> AB;

我希望 AB 必须按顺序访问 A 的内容和 B 的内容。做这样的事情最有效的方法是什么?

【问题讨论】:

  • 如果在使用大型容器时寻求效率,使用列表可能更有效,您可以通过多个指针操作将一个拼接到另一个。但是列表有空间开销(考虑使用单链表)。
  • 这能回答你的问题吗? Concatenating two std::vectors

标签: c++ vector


【解决方案1】:

如果您的向量已排序*,请查看 set_union&lt;algorithm&gt;

set_union(A.begin(), A.end(), B.begin(), B.end(), AB.begin());

链接中有一个更详尽的示例。

【讨论】:

  • 另外,它与直接追加不同 - 输出范围内的元素是唯一的,这可能不是 OP 想要的(它们甚至可能无法比较)。这当然不是最有效的方法。
【解决方案2】:
AB.reserve( A.size() + B.size() ); // preallocate memory
AB.insert( AB.end(), A.begin(), A.end() );
AB.insert( AB.end(), B.begin(), B.end() );

【讨论】:

  • 谢谢!不会想到保留。
  • 它应该复制每个元素,所以它是 O(n)
  • 不确定是否要提出一个新问题,但是在考虑移动语义时可以改进这个答案吗?有什么方法可以期望/指示编译器执行一次内存移动而不是循环遍历所有元素?
  • @boycy 否。push_back 一个元素是摊销的常数时间。推回 n 个元素是 O(n)
  • @Konrad 我没有其他暗示,但感谢您的澄清。请注意,插入操作的复杂性永远不会以插入元素的数量来给出 - 这总是会给出 O(n) - 而是以已经在容器中的元素数量来表示,因为这提供了衡量其可扩展性的标准。​​
【解决方案3】:

这正是成员函数std::vector::insert的用途

std::vector<int> AB = A;
AB.insert(AB.end(), B.begin(), B.end());

【讨论】:

  • @Nick:比什么慢?
  • 也许它检查每个插入元素的空间是否足够?提前使用储备会加快速度。
  • @Nick:如果每个现代 stdlib 实现都将 insert 专门用于随机访问迭代器并预先保留,我不会感到惊讶。
  • @Gman:这是一个公平的观点,因为我们知道源也是一个向量(其中迭代器 distance 具有 O(1) 复杂度)。尽管如此,insert 的性能保证是需要注意的,因为您通常可以通过提前计划做得更好。
  • @RvdK 检查空间只是几个指令:加载容量,比较大小,条件跳转;在大多数情况下,所有这些成本都可以忽略不计。由于size &lt; capacity 在大多数情况下,分支预测可能会导致非重新分配分支的指令位于指令流水线中,从而最大限度地减少分支引起的延迟(除了低迭代次数外)。这假设了一个好的向量实现,加上 CPU 指令管道和 [好的] 分支预测,但对于现代工具链和台式机来说,这些都是非常可靠的假设。虽然不知道智能手机..
【解决方案4】:

取决于您是否真的需要将两个向量物理连接起来,或者您想要给出连接的外观以进行迭代。 boost::join 函数

http://www.boost.org/doc/libs/1_43_0/libs/range/doc/html/range/reference/utilities/join.html

会给你这个。

std::vector<int> v0;
v0.push_back(1);
v0.push_back(2);
v0.push_back(3);

std::vector<int> v1;
v1.push_back(4);
v1.push_back(5);
v1.push_back(6);
...

BOOST_FOREACH(const int & i, boost::join(v0, v1)){
    cout << i << endl;
}

应该给你

1
2
3
4
5
6

注意 boost::join 不会将两个向量复制到新容器中 但会生成一对覆盖范围的迭代器(范围) 两个容器。会有一些性能开销,但也许 不如先将所有数据复制到新容器中。

【讨论】:

  • 好主意。想了一会儿,我意识到这个目标也可以在不使用 boost 库的情况下实现。我已经发布了一个解释如何的答案。
【解决方案5】:

基于Kiril V. Lyadvinsky answer,我做了一个新版本。这个 sn -p 使用模板和重载。有了它,你可以写vector3 = vector1 + vector2vector4 += vector3。希望对您有所帮助。

template <typename T>
std::vector<T> operator+(const std::vector<T> &A, const std::vector<T> &B)
{
    std::vector<T> AB;
    AB.reserve(A.size() + B.size());                // preallocate memory
    AB.insert(AB.end(), A.begin(), A.end());        // add A;
    AB.insert(AB.end(), B.begin(), B.end());        // add B;
    return AB;
}

template <typename T>
std::vector<T> &operator+=(std::vector<T> &A, const std::vector<T> &B)
{
    A.reserve(A.size() + B.size());                // preallocate memory without erase original data
    A.insert(A.end(), B.begin(), B.end());         // add B;
    return A;                                        // here A could be named AB
}

【讨论】:

  • 你的意思是把每个向量的元素相互相加吗?或者你的意思是追加?现在很清楚,但在接下来的 5 年里..?如果含义不明确,则不应重载运算符。
  • @S.R 我的意思是连接。我在 3 年前写了这个答案。我仍然知道这意味着什么。那里没问题。如果 C++ 可以提​​供自己的重载,那就更好了。 (是的,:: 被占用了;)
  • 一般情况下肯定不清楚v1 + v2不代表加法。
  • @Apollys well
  • 替代方案是使用@,就像在 F# 中一样
【解决方案6】:

所有解决方案都是正确的,但我发现编写一个函数来实现它更容易。像这样:

template <class T1, class T2>
void ContainerInsert(T1 t1, T2 t2)
{
    t1->insert(t1->end(), t2->begin(), t2->end());
}

这样你就可以避免这样的临时放置:

ContainerInsert(vec, GetSomeVector());

【讨论】:

    【解决方案7】:

    还有一个尚未提及的简单变体:

    copy(A.begin(),A.end(),std::back_inserter(AB));
    copy(B.begin(),B.end(),std::back_inserter(AB));
    

    并使用合并算法:

    #include <algorithm> #include <vector> #include <iterator> #include <iostream> #include <sstream> #include <string> template<template<typename, typename...> class Container, class T> std::string toString(const Container<T>& v) { std::stringstream ss; std::copy(v.begin(), v.end(), std::ostream_iterator<T>(ss, "")); return ss.str(); }; int main() { std::vector<int> A(10); std::vector<int> B(5); //zero filled std::vector<int> AB(15); std::for_each(A.begin(), A.end(), [](int& f)->void { f = rand() % 100; }); std::cout << "before merge: " << toString(A) << "\n"; std::cout << "before merge: " << toString(B) << "\n"; merge(B.begin(),B.end(), begin(A), end(A), AB.begin(), [](int&,int&)->bool {}); std::cout << "after merge: " << toString(AB) << "\n"; return 1; }

    【讨论】:

      【解决方案8】:

      按照 Bradgonesurfing 的回答,很多时候并不真正需要连接两个向量 (O(n)),而只是 像连接它们一样使用它们(O(1))。如果这是您的情况,则无需 Boost 库即可完成。

      诀窍是创建一个向量代理:一个包装类,它操作两个向量的引用,在外部被视为一个单独的连续向量。

      用法

      std::vector<int> A{ 1, 2, 3, 4, 5};
      std::vector<int> B{ 10, 20, 30 };
      
      VecProxy<int> AB(A, B);  // ----> O(1). No copies performed.
      
      for (size_t i = 0; i < AB.size(); ++i)
          std::cout << AB[i] << " ";  // 1 2 3 4 5 10 20 30
      

      实施

      template <class T>
      class VecProxy {
      private:
          std::vector<T>& v1, v2;
      public:
          VecProxy(std::vector<T>& ref1, std::vector<T>& ref2) : v1(ref1), v2(ref2) {}
          const T& operator[](const size_t& i) const;
          const size_t size() const;
      };
      
      template <class T>
      const T& VecProxy<T>::operator[](const size_t& i) const{
          return (i < v1.size()) ? v1[i] : v2[i - v1.size()];
      };
      
      template <class T>
      const size_t VecProxy<T>::size() const { return v1.size() + v2.size(); };
      

      主要好处

      创建它的时间为 O(1)(恒定时间),并且额外的内存分配最少。

      需要考虑的一些事项

      • 只有在您真正了解自己在处理参考资料时所做的事情时,才应该这样做此解决方案是针对所提出问题的特定目的而设计的,效果很好。如果您不确定引用的工作原理,在任何其他情况下使用它可能会导致意外行为。
      • 在此示例中,AB 确实 提供非常量 访问运算符 ([ ])。随意包含它,但请记住:因为 AB 包含引用,分配它 值也会影响 A 和/或 B 中的原始元素。无论这是否是 理想的功能,这是一个特定于应用程序的问题,应该 仔细考虑。
      • 直接对 A 或 B 进行的任何更改(例如分配值、 排序等)也将“修改”AB。这不一定是坏事 (实际上,它可以非常方便:AB 永远不需要显式 更新以保持自身与 A 和 B 同步),但它是 当然是必须注意的一种行为。重要的例外:将 A 和/或 B 的大小调整为……更大可能会导致它们在内存中重新分配(为了需要连续空间),这反过来会使 AB 失效。
      • 因为对元素的每次访问都先于一个测试(即“i
      • 这种方法可以推广到 n 个向量。我没试过,但是 应该没什么大不了的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-03-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-09-27
        相关资源
        最近更新 更多