15

我有vector<vector<int> > Y。我想将 Y 内的子向量(称为 y)合并为 one vector<int>。但我不想对它们进行排序,即按它们出现的顺序合并它们。也许通过使用 STL 算法,我将如何有效地做到这一点?该std::merge方法通过排序合并我不想要的。

编辑:我想要的是:给定 {{1,6,5},{5,3-1,77},{0},...} 返回 {1,6,5,5,3,-1, 77,0,...}

4

2 回答 2

20

这个词是concatenationflatten

std::vector<int> a { 1,2,3 },
                 b {9,0,-7};

std::vector<int> c(begin(a), end(a));
c.insert(end(c), begin(b), end(b));

或者,确实更简单:

auto c = a;
c.insert(end(c), begin(b), end(b));

c现在包含 1,2,3,9,0,-7

您可以将其概括为处理您的嵌套容器案例:

template <template<typename...> class R=std::vector, 
          typename Top, 
          typename Sub = typename Top::value_type> 
   R<typename Sub::value_type> flatten(Top const& all)
{
    using std::begin;
    using std::end;

    R<typename Sub::value_type> accum;

    for(auto& sub : all)
        accum.insert(end(accum), begin(sub), end(sub));

    return accum;
}

如果您想将元素从第一个容器移动到最后一个容器(如果元素只能移动,或者复制成本很高),请使用std::movestd::back_inserter应用于std::make_move_operator每个源迭代器。

在Coliru上现场观看

更新:可变参数连接

最初,我预计您会使用可变参数解决方案:让我演示如何使这个解决方案更通用,因此您可以说:

 auto x = to_vector(std::vector<int> { 1,2,3 }, std::list<int> { 9,8,11 }, std::set<int> { 42 });

事实上,我把它说得很笼统,以至于你将异构集合连接到“任意”容器中:

// fun with maps:
auto y = concatenate<std::map<long, std::string> >(
        std::map<int,      const char*> { { 1, "one" }, { 2, "two" } },
        std::map<unsigned, std::string> { { 1, "one" }, { 3, "three" } }            
    );

您会(正确地)期望这to_vector只是concatenate<std::vector<...>>. 这是完整的蒙蒂,Coliruideone上看到它:

#include <vector>
#include <utility>
#include <iterator>

namespace detail
{
    template <typename R>
    void do_concatenation(R& accum) {}

    template <typename R, typename First, typename... More> 
    void do_concatenation(R& accum, First const& first, More const&... more)
    {
        using std::begin;
        using std::end;
        std::copy(begin(first), end(first), std::inserter(accum, end(accum)));

        do_concatenation(accum, more...);
    }
}

template <typename Result, typename... Containers> 
   Result concatenate(Containers const&... containers)
{
    Result accum;
    detail::do_concatenation(accum, containers...);
    return accum;
}

template <typename First, typename... More> 
   std::vector<typename First::value_type> to_vector(First const& first, More const&... containers)
{
    return concatenate<std::vector<typename First::value_type>>(first, containers...);
}

/// demo
#include <set>
#include <list>
#include <iostream>

#include <map>
#include <string>

int main()
{
    auto x = to_vector(std::vector<int> { 1,2,3 }, std::list<int> { 9,8,11 }, std::set<int> { 42 });

    for (auto i : x)
       std::cout << i << " ";

    std::cout << std::endl;

    // fun with maps:
    auto y = concatenate<std::map<long, std::string> >(
            std::map<int,      const char*> { { 1, "one" }, { 2, "two" } },
            std::map<unsigned, std::string> { { 1, "one" }, { 3, "three" } }            
        );

    for (auto kvp : y)
       std::cout << "(" << kvp.first  << ", " << kvp.second << ")";

}

输出:

1 2 3 9 8 11 42 
(1, one)(2, two)(3, three)
于 2013-06-25T10:19:22.613 回答
15
template <typename T>
std::vector<T> flatten(const std::vector<std::vector<T>>& v) {
    std::size_t total_size = 0;
    for (const auto& sub : v)
        total_size += sub.size(); // I wish there was a transform_accumulate
    std::vector<T> result;
    result.reserve(total_size);
    for (const auto& sub : v)
        result.insert(result.end(), sub.begin(), sub.end());
    return result;
}

与@not-sehe 的解决方案相比,这具有一些性能优势:

  • 它计算结果的最终大小并预分配存储空间。这意味着不需要重复重新分配。
  • 它使用范围插入而不是像其他版本那样使用单元素插入。这允许容器应用一些优化,特别是如果元素是可简单复制的(即每个内部容器一个 memcpy)。

不利的一面是,它仅适用于向量,而不适用于双端队列或列表。

于 2013-06-25T14:11:01.113 回答