仅使用键作为比较器来交叉两个映射

时间:2009-11-04 18:11:19

标签: c++ stl

我有两张地图,我想得到的地图是仅使用键作为比较器的两个交点,同时对常见元素的值进行简单的数学运算,例如+/-

例如:

map<int, double> m1,m2;
m1[1] = 1.1;
m1[2] = 2.2

m2[2] = 0.1;
m2[4] = 3.3;

交叉后我会得到m3,如果我使用减法运算符,它将具有对:(2,2.1)。

使用算法库的有效方法是什么?

谢谢。

3 个答案:

答案 0 :(得分:4)

我们希望在此功能中执行哪些操作?

  • 遍历地图
  • 合并具有相同键的值
  • 向地图添加元素

然后我们必须考虑除了std :: map之外的其他容器是否适合这个模型。您想要包含多重映射并将所有元素与相同的键组合在一起(假设不是)?地图不必排序,因此散列图也应该有效。不幸的是,没有STL概念包括地图和哈希图,但没有包含多图。因此,让我们做一个:独特的对关联容器。我们的功能应该适用于这两种类型。

template <typename UPAC, // UPAC models Unique Pair Associative Container
          typename BF>   // BF models Binary Function on UPAC::value_type
UPAC combine_upacs(const UPAC& c1, const UPAC& c2, BF func) {
  UPAC result;
  typename UPAC::const_iterator it1 = c1.begin();
  while (it1 != c1.end()) {
    typename UPAC::const_iterator it2 = c2.find(it1->first);
    if (it2 != c2.end())
      result.insert(make_pair(it1->first, func(it1->second,it2->second));
    ++it1;
  }
  return result;
}

现在,如果您担心merge_upacs在std :: map上的运行时间,您可能希望利用映射进行排序的事实,并迭代c1和c2。但这是解决问题的最通用的代码。

答案 1 :(得分:3)

你可以分两步完成。第一个是找到交集,可以使用set_intersection算法完成。第二步将使用transform算法使用提供的仿函数进行数学运算。


我发现set_intersection不允许提供访问函数,因此在下面的代码中有一个懒惰的替代品。我写了一个样本函子,它会为你做减法。我相信你可以轻松编写所有其他的仿函数。你也可以编写与set_intersection做同样的模板函数,但允许提供将取消引用迭代器的函子。

// sample functor for substruction
template<typename T1, typename T2>
struct sub
{
  // it will be better to use const references, but then you'll not be able
  // to use operator[], and should be use `find` instead 
  sub( std::map<T1, T2>& m1, std::map<T1, T2>& m2 ) : m1(m1), m2(m2) {}
  std::pair<T1,T2> operator()( const T1& index )
  { return make_pair( index, m1[index]-m2[index] ); }    
private:
  std::map<T1, T2>& m1, & m2;
};

int main()
{
 map<int, double> m1,m2;
 m1[1] = 1.1;
 m1[2] = 2.2;

 m2[2] = 0.1;
 m2[4] = 3.3;

 vector<int> v; // here we will keep intersection indexes

 // set_intersection replacement
 // should be moved to stand alone function
 map<int, double>::const_iterator begin1 = m1.begin();
 map<int, double>::const_iterator begin2 = m2.begin();
 for (; begin1 != m1.end() && begin2 != m2.end(); ) {
  if ( begin1->first < begin2->first ) ++begin1;
  else if (begin2->first < begin1->first) ++begin2;
  else v.push_back( begin1->first ), ++begin1, ++begin2;
 }

 map<int, double> m3;
 transform( v.begin(), v.end(), std::inserter(m3, m3.begin()), sub<int, double>( m1, m2 ) );
}

答案 2 :(得分:0)

我不知道这样做的标准算法,但编写一个很简单。这适用于有序和无序地图。

  template <class MapT, typename OperationT>
  void intersect_maps(const MapT &map1, const MapT &map2, MapT &target, OperationT op) {
  const MapT *m1, *m2;
  // Pick the smaller map to iterate over
  if (map1.size() < map2.size()) {
    m1 = &map1;
    m2 = &map2;
  } else {
    m1 = &map2;
    m2 = &map1;
  }
  typename MapT::const_iterator it_end = m1->end();
  for (typename MapT::const_iterator it = m1->begin(); it != it_end; ++it) {
    typename MapT::const_iterator pos = m2->find(it->first);
    if (pos != m2->end()) {
      if (m1 == &map1) {
        target.insert(typename MapT::value_type(it->first, op(it->second, pos->second)));
      } else {
        // we swapped the inputs so we need to swap the operands
        target.insert(typename MapT::value_type(it->first, op(pos->second, it->second)));
      }
    }
  }
}

double sub(double v1, double v2) {
  return v1 - v2;
}

// And use it.    
m1[1] = 1.1;
m1[2] = 2.2;

m2[2] = 0.1;
m2[4] = 3.3;

intersect_maps(m1, m2, m3, sub);