将向量的向量合并为单个向量

Posted

技术标签:

【中文标题】将向量的向量合并为单个向量【英文标题】: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,因此您可能会在标准中看到它……有一天。 【参考方案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 - 这已经是现有的标准算法。不知道是不是更快。

【讨论】:

以上是关于将向量的向量合并为单个向量的主要内容,如果未能解决你的问题,请参考以下文章

将 R 中的数据帧连接/合并为向量类型单元格

将两个排序向量合并到一个排序向量中

使用R语言将不同长度的向量合并为数据框

一种将不同长度的命名向量合并到 R 中的数据框(将名称信息保留为列名)的快速方法

Matlab两个一维数组合并为一个数组?

C++ 合并和排序 2 个向量