【问题标题】:How to concatenate few maps?如何连接几张地图?
【发布时间】:2013-01-20 10:25:03
【问题描述】:

我的地图很少std::map< char, int >

First map:
a - 1
b - 2
c - 3

Second map:
a - 5
c - 6
e - 7

我想将它们连接成std::map< char, std::vector< int > >

a - 1 5
b - 2 0
c - 3 6
e - 0 7

最好的方法是什么?

【问题讨论】:

标签: c++ algorithm map stl merge


【解决方案1】:

首先想到的是std::merge算法。不幸的是,源和目标范围的值类型不兼容,所以我们需要一些可以为我们转换它的东西。 Boost 通过Function Output Iterator 提供了一个很好的工具。分配给此输出迭代器的任何内容都作为参数传递给它包装的一元函数。与 lambdas 一起,这非常简单:

#include <boost/function_output_iterator.hpp>

std::map<char, int> m1 { {'a',1}, {'b',2}, {'c',3} };
std::map<char, int> m2 { {'a',5}, {'c',6}, {'e',7} };

std::map<char, std::vector<int>> m3;

typedef std::map<char, int>::value_type source_type;
auto push_value =
    [&m3](const source_type& p) { m3[p.first].push_back(p.second); };

std::merge(m1.begin(), m1.end(), m2.begin(), m2.end(),
    boost::make_function_output_iterator(push_value));

这还不是我们想要的。 m3 看起来像这样:

a - 1 5
b - 2
c - 3 6
e - 7

对于在m2 但不在m1 中的键,我们需要在向量的前面挤压一个零。我们可以在合并之前使用set_difference 来做到这一点。我们需要使用只比较地图键的自定义比较器:

auto push_zero =
    [&m3](const source_type& p) { m3[p.first].push_back(0); };
auto cmp =
    [](const source_type& p1, const source_type& p2) { return p1.first < p2.first; };

std::set_difference(m2.begin(), m2.end(), m1.begin(), m1.end(),
    boost::make_function_output_iterator(push_zero), cmp);

m3 现在是:

a - 1 5
b - 2
c - 3 6
e - 0 7

在第三步中,我们为m1 中但不在m2 中的键添加一个零:

std::set_difference(m1.begin(), m1.end(), m2.begin(), m2.end(),
    boost::make_function_output_iterator(push_zero), cmp);

现在我们得到了我们想要的:

a - 1 5
b - 2 0
c - 3 6
e - 0 7

请参阅LiveWorkspace 上的完整示例。

【讨论】:

  • +1 这比我自己的两步显式 for 循环方便得多。
【解决方案2】:

天真的方法是首先在目标映射中添加所有键。然后为目标映射中的每个键添加第一个映射中的相应值,如果未找到该键,则添加零。然后对第二张地图做同样的事情。

【讨论】:

  • 是的。这是我首先想到的,但我没有加零,而是考虑使用 vector::resize()。
  • 在步骤 1 中添加的每个 vector 上调用 .reserve(NumMaps) 可能是明智之举。您确切知道它们会变得多大。
【解决方案3】:

这样的辅助函数怎么样:

void one_map ( const std::map <char, int> &source, std::map<char, std::vector<int> > &dest, size_t idx )
    for ( auto const &p : source )
        dest [ p.first ] [ idx ] += 1;
    }

void fill_map ( const std::map <char, int> &source, std::map<char, std::vector<int> &dest , const std::vector<int> &zeroes ) {
    for ( auto const &p : source )
        if ( !dest.find ( p.first ))
            dest [ p.first ] = zeroes;
    }

然后你可以写:

std::vector<int> z (3, 0);  // three zeros
fill_map ( a, dest, z );
fill_map ( b, dest, z );
fill_map ( c, dest, z );

one_map  ( a, dest, 0 );
one_map  ( b, dest, 1 );
one_map  ( c, dest, 2 );

【讨论】:

    【解决方案4】:

    对于它的价值,这是一个简单的无提升解决方案,它可能会运行得更快一些(不是在大 O 中,而是在总迭代次数中):

       std::map<char,int>::iterator i,j;
       i = m1.begin(); j = m2.begin();
    
       while (i!=m1.end() || j!=m2.end()) {
          if (j==m2.end() || (i!=m1.end()&&(i->first < j->first))) {
             m3[i->first].push_back(i->second);
             m3[i->first].push_back(0);
             i++;
          } else if (i==m1.end() || (i->first > j->first)) {
             m3[j->first].push_back(0);
             m3[j->first].push_back(j->second);
             j++;         
          } else if (i->first == j->first) {
             m3[i->first].push_back(i->second);
             m3[j->first].push_back(j->second);
             i++;j++;
          } 
       }
    

    可能可以简化以减少代码行数,因为 push_backs 每次执行 3 次(3 种不同情况)......

    这里的运行时间是 (# in m1)+(# in m2)-(# in both)。很可能,这大致等于单个集合差异(或单个合并)。

    Liveworkspace

    【讨论】:

      猜你喜欢
      • 2023-03-14
      • 2013-04-04
      • 1970-01-01
      • 2017-07-27
      • 2019-01-05
      • 2020-03-05
      • 2019-05-06
      • 2010-09-12
      • 1970-01-01
      相关资源
      最近更新 更多