【问题标题】:Merge vector of vectors into a single vector将向量的向量合并为单个向量
【发布时间】:2016-05-19 04:25:12
【问题描述】:

我有 T 的向量向量:

std::vector<std::vector<T>> vector_of_vectors_of_T;

我想将它们全部合并到 T 的单个向量中:

std::vector<T> vector_of_T;

我目前正在使用这种方法:

size_t total_size{ 0 };
for (auto const& items: vector_of_vectors_of_T){
    total_size += items.size();
}
vector_of_T.reserve(total_size);
for (auto const& items: vector_of_vectors_of_T){
    vector_of_T.insert(end(vector_of_T), begin(items), end(items));
}

还有更直接的方法吗?像一个准备好的标准功能?如果没有,是否有更有效的手动方式?

【问题讨论】:

  • 我认为这个问题更适合codereview.stackexchange.com
  • @Luca Pizzamiglio 感谢您的纠正
  • vector_of_vectors_of_T.SelectMany(... -- 哦等等,语言错误:P
  • @Humam 如果您想获得太多 STL,您可以将其用于 reserve 部分:vector_of_T.reserve(std::accumulate(std::begin(vector_of_vectors_of_T), std::end(vector_of_vectors_of_T), 0, [](size_t size, std::vector&lt;T&gt; const&amp; vec) { return size + vec.size(); }));
  • 这里没有太大的改进空间,除了使用移动迭代器(如果你不再需要原来的)。这是 range-v3 中的 action::join,因此您可能会在标准中看到它……有一天。

标签: c++ algorithm c++11 vector


【解决方案1】:

尝试编写一个通用的join 是一个很好的练习。下面的代码采用嵌套容器R1&lt;R2&lt;T&gt; 并返回一个连接容器R1&lt;T&gt;。请注意,由于标准库中的分配器参数,这有点麻烦。没有尝试检查分配器的兼容性等。

幸运的是,Eric Niebler 即将推出的 range-v3 库中有 action::join 函数,该函数已经非常强大,并且现在可以在 Clang 上运行:

#include <range/v3/all.hpp>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <numeric>
#include <vector>

// quick prototype
template<template<class, class...> class R1, template<class, class...> class R2, class T, class... A1, class... A2>
auto join(R1<R2<T, A2...>, A1...> const& outer)
{
    R1<T, A2...> joined;
    joined.reserve(std::accumulate(outer.begin(), outer.end(), std::size_t{}, [](auto size, auto const& inner) {
        return size + inner.size();
    }));
    for (auto const& inner : outer)
        joined.insert(joined.end(), inner.begin(), inner.end());
    return joined;
}

int main()
{
    std::vector<std::vector<int>> v = { { 1, 2 }, { 3, 4 } };

    // quick prototype
    std::vector<int> w = join(v);
    std::copy(w.begin(), w.end(), std::ostream_iterator<int>(std::cout, ",")); std::cout << "\n";

    // Eric Niebler's range-v3
    std::vector<int> u = ranges::action::join(v);
    std::copy(u.begin(), u.end(), std::ostream_iterator<int>(std::cout, ",")); std::cout << "\n";
}

Live Example

【讨论】:

  • 顺便说一句,这种“可连接容器”的数学术语是monad。 (这个概念比你可以用迭代器编写的任何东西都更笼统。)
【解决方案2】:

使用back_insertermove

size_t total_size{ 0 };
for (auto const& items: vector_of_vectors_of_T){
    total_size += items.size();
}

vector_of_T.reserve(total_size);
for (auto& items: vector_of_vectors_of_T){    
    std::move(items.begin(), items.end(), std::back_inserter(vector_of_T));
}

而不是copyingstd::move 给了它一点性能增强。

【讨论】:

  • 没有基准测试,从抽象的角度来看,它应该比我的更快吗?之前也需要预留吗?
  • 这可能比带有移动迭代器的范围插入版本效率低,特别是对于可复制的元素类型(范围插入可能会进行大量优化)。
【解决方案3】:

我想您可以尝试在循环中使用std::merge/std::move - 这已经是现有的标准算法。不知道是不是更快。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-25
    • 2021-12-06
    • 2022-01-02
    • 1970-01-01
    相关资源
    最近更新 更多