使用键作为比较器,交集两个地图

4
我是一名有用的助手,可以为您进行文本翻译。

我有两张地图,希望只使用键作为比较器来获取这两张地图的交集,并且在共同元素的值上进行简单的数学运算,如加减。

例如:

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个回答

4

我们希望在这个函数中执行哪些操作?

  • 遍历映射表
  • 合并具有相同键的值
  • 向映射表添加元素

然后,我们必须考虑除了std::map之外还有哪些容器适合这种模型。您想包括multimaps并将具有相同键的所有元素组合在一起吗(假设不是)?该映射不必排序,因此哈希映射表也应该可以工作。不幸的是,没有STL概念包括maps和哈希maps但不包括multimaps。因此,让我们自己创造一个:Unique Pair Associative Container。我们的函数应该适用于这两种类型。

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;
}

如果你担心在std::map上运行combine_upacs的时间,你可以利用maps是有序的这一事实,通过迭代c1和c2来解决问题。但这是解决问题的最通用代码。


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 ) );
}

谢谢。实际上,我正在寻找几行代码中简单而又强大的东西。例如,vector<int>::iterator i = d12.begin(); insert_iterator<vector<int> > insertiter(d12, i); set_intersection(d1.begin(), d1.end(), d2.begin(), d2.end(), insertiter);这将适用于两个向量以获取交集。但是,对于映射,我无法弄清楚,因为在映射中比较的是一对,而不是键,我可以创建自定义比较器来处理映射,但仍然需要在某个地方使用<minus>/<plus>函数.... - user202824
Kirill,感谢您的评论。这正是我所需要的。 - user202824

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);

如果您交换了输入映射,则需要交换操作数。如果没有测试,结果为m3 [2] = -2.1。 - jon hanson
是的 - 这就是我匆忙得到的。已经解决了。 - sdtom
已经固定了,但每次检查m1与&map1都是低效的,因为在任何给定的调用中答案都不会改变,但编译器无法优化掉它。 - Mark Ruzon
这是一个额外的检查,但你真的必须对程序进行分析以评估这些微观优化的实际影响。我重写了函数以将检查放在循环外,并将运行时间与发布版本进行了比较。当m1中有1000万个元素,m2中有100万个元素,共有333,333个元素时,平均加速比约为0.5%。 - sdtom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接