【问题标题】:How to union two sorted vectors and combine overlapping elements?如何合并两个排序的向量并组合重叠的元素?
【发布时间】:2014-01-08 19:04:02
【问题描述】:

typedef pair<int, double> Element;

然后我有两个向量:

vector<Element> A, B;

这些向量按Element.first 中的整数排序。我想得到第三个向量C,它是AB 的并集。这听起来像set_union,但是当A[i].first == B[j].first 时我需要不同的行为。 set_union 将简单地选择要包含在C 中的源元素之一,但我需要结果来“组合”这两个元素。换句话说,是这样的:

C[k].first = A[i].first; // == B[j].first.  set_union does this
C[k].second = A[i].second + B[j].second; // set_union does NOT do this.

我很感兴趣是否可以使用标准库(或类似 Boost)。手动执行此操作的代码并不是特别复杂,但我不想重新发明轮子。

我能找到的唯一其他相关操作是merge。它也不合并元素,并且会涉及另一个合并过程。

【问题讨论】:

  • std::merge 可以用一个非常聪明的输出迭代器做到这一点,但我认为一个新的算法会更好
  • @MooingDuck 你在想back_inserter 这样的东西,但它会检查新元素是否与前一个元素相同?
  • 我不敢苟同...这里的目标被误导了,标准算法和花哨的迭代器的任何“聪明”应用程序都会写得更慢,就像冗长和令人眼花缭乱一样复杂亚历克斯答案风格的明显循环方法。
  • @Praetorian:如果您创建一个包含对容器的引用的迭代器,它可以查看该容器的最后一个元素并将其与正在传递的元素进行比较,并决定是否应该推送将新元素添加到容器的末尾,或将其与前一个元素组合。
  • 好的 - 我不明白boost::function_output_iterator 多么适合这个问题......同意这是一个很好的解决方案,可以在桌面上进行提升。 (否则,必须编写一个返回代理来捕获分配的迭代器是不值得大惊小怪的......)

标签: c++ boost c++11 vector set


【解决方案1】:

我认为std::mergeboost::function_output_iterator 的使用非常干净。

#include <algorithm>
#include <iostream>
#include <vector>

#include <boost/function_output_iterator.hpp>

/* Convenience type alias for our element. */
using Elem = std::pair<int, double>;

/* Convenience type alias for the container of our elements. */
using Elems = std::vector<Elem>;

/* Our appender that will be created with boost::function_output_iterator. */
class Appender {
  public:

  /* Cache the reference to our container. */
  Appender(Elems &elems) : elems_(elems) {}

  /* Conditionally modify or append elements. */
  void operator()(const Elem &elem) const {
    if (!elems_.empty() && elems_.back().first == elem.first) {
      elems_.back().second += elem.second;
      return;
    }  // if
    elems_.push_back(elem);
  }

  private:

  /* Reference to our container. */      
  Elems &elems_;

};  // Appender

int main() {
  // Sample data.
  Elems lhs {{1, 2.3}, {2, 3}, {5, 3.4}};
  Elems rhs {{1, 1.3}, {3, 5.5}, {4, 2.2}};
  Elems result;
  // Merge and use appender to append elements.
  std::merge(std::begin(lhs),
             std::end(lhs),
             std::begin(rhs),
             std::end(rhs),
             boost::make_function_output_iterator(Appender(result)));
  // Print result.
  for (const auto &elem : result) {
    std::cout << elem.first << ' ' << elem.second << std::endl;
  }  // for
}

打印:

1 3.6
2 3
3 5.5
4 2.2
5 3.4

注意。 function_output_iterator 的使用是由Benjamin Lindley 建议的。

【讨论】:

    【解决方案2】:

    这是一个独立的通用算法merge_elements的实现:

    #include <algorithm>
    #include <utility>
    
    template <typename LInput, typename RInput, typename Output>
    Output merge_elements(LInput lbegin, LInput lend,
                          RInput rbegin, RInput rend,
                          Output out) {
        while(true) {
            if (lbegin == lend) {
                return std::copy(rbegin, rend, out);
            }
    
            if (rbegin == rend) {
                return std::copy(lbegin, lend, out);
            }
    
            if (lbegin->first < rbegin->first) {
                *out++ = *lbegin++;
            } else if (rbegin->first < lbegin->first) {
                *out++ = *rbegin++;
            } else {
                *out++ = std::make_pair(lbegin->first, lbegin->second + rbegin->second);
                ++lbegin;
                ++rbegin;
            }
        }
    }
    
    #include <iostream>
    #include <iterator>
    #include <vector>
    
    /* Convenience type alias for our element. */
    using Elem = std::pair<int, double>;
    
    /* Convenience type alias for the container of our elements. */
    using Elems = std::vector<Elem>;
    
    int main() {
      // Sample data.
      Elems lhs {{1, 2.3}, {2, 3}, {5, 3.4}};
      Elems rhs {{1, 1.3}, {3, 5.5}, {4, 2.2}};
      Elems result;
      // Merge and use appender to append elements.
      merge_elements(std::begin(lhs),
                     std::end(lhs),
                     std::begin(rhs),
                     std::end(rhs),
                     std::back_inserter(result));
      // Print result.
      for (const auto &elem : result) {
        std::cout << elem.first << ' ' << elem.second << std::endl;
      }  // for
    }
    

    它不需要 boost,但总行数与 mpark 的 boost 解决方案几乎完全相同。有趣的是,这个算法足够通用,可以在 std::map&lt;int,double&gt;std::vector&lt;std::pair&lt;int,double&gt;&gt; 上保持不变:

    #include <iostream>
    #include <iterator>
    #include <map>
    
    /* Convenience type alias for the container of our elements. */
    using Elems = std::map<int, double>;
    
    int main() {
      // Sample data.
      Elems lhs {{1, 2.3}, {2, 3}, {5, 3.4}};
      Elems rhs {{1, 1.3}, {3, 5.5}, {4, 2.2}};
      Elems result;
      // Merge and use appender to append elements.
      merge_elements(std::begin(lhs),
                     std::end(lhs),
                     std::begin(rhs),
                     std::end(rhs),
                     std::inserter(result, result.begin()));
      // Print result.
      for (const auto &elem : result) {
        std::cout << elem.first << ' ' << elem.second << std::endl;
      }  // for
    }
    

    【讨论】:

      猜你喜欢
      • 2014-04-17
      • 1970-01-01
      • 1970-01-01
      • 2021-10-11
      • 1970-01-01
      • 2014-04-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多