简体   繁体   English

如何结合两个排序的向量并组合重叠元素?

[英]How to union two sorted vectors and combine overlapping elements?

Let

typedef pair<int, double> Element;

I then have two vectors: 然后我有两个向量:

vector<Element> A, B;

These vectors are sorted by the integer in Element.first . 这些向量按Element.first的整数排序。 I want to get a third vector, C , which is the union of A and B . 我想得到第三个向量C ,它是AB A并集。 This sounds like set_union , but I need different behavior when A[i].first == B[j].first . 这听起来像set_union ,但是当A[i].first == B[j].first .first时我需要不同的行为。 set_union will simply choose one of the source elements to include in C , but I need the result to "combine" the two elements instead. set_union将简单地选择要包含在C一个源元素,但我需要结果来“组合”这两个元素。 In other words, something like this: 换句话说,这样的事情:

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.

I'm interested if this is possible using the standard library (or something like Boost). 我很感兴趣,如果可以使用标准库(或像Boost这样的东西)。 The code to do this manually is not particularly complicated, but I'd like to not re-invent the wheel. 手动执行此操作的代码并不是特别复杂,但我不想重新发明这个轮子。

The only other related operation I can find is merge . 我能找到的唯一其他相关操作是合并 It doesn't merge elements either, and would involve another combining pass. 它也不合并元素,并涉及另一个组合传递。

I think the use of std::merge with boost::function_output_iterator is pretty clean. 我认为使用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
}

Prints: 打印:

1 3.6
2 3
3 5.5
4 2.2
5 3.4

Note. 注意。 The use of function_output_iterator was suggested by Benjamin Lindley . Benjamin Lindley建议使用function_output_iterator

Here's an implementation with a standalone generic algorithm merge_elements : 这是一个带有独立通用算法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
}

It doesn't require boost, but has almost exactly the same total line count as mpark's boost solution. 它不需要增强,但与mpark的增强解决方案几乎完全相同。 Interestingly, this algorithm is generic enough to work unchanged with a std::map<int,double> as well as std::vector<std::pair<int,double>> : 有趣的是,这个算法非常通用,可以与std::map<int,double>以及std::vector<std::pair<int,double>>

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM