是否存在以以下方式对两个范围进行分类和划分的标准算法?

时间:2019-04-15 11:21:35

标签: c++ algorithm stl

我想知道以下算法的标准方法是否存在。我想对两个范围xy进行排序和分区。 xy都应使用二进制谓词进行分区,该谓词采用xy中的元素。该函数的签名应类似于:

template<typename ForwardIt1, typename ForwardIt2, typename Compare, typename BinaryPredicate>
std::pair<ForwardIt1, ForwardIt2> sort_and_partition(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2, ForwardIt2 last2, Compare comp, BinaryPredicate p);

这里comp转发到std::sort。二进制谓词p用于分区。然后,例如:

std::vector<T> x;
std::vector<T> y;

auto [xm, ym] = sort_and_partition(x.begin(), x.end(), y.begin(), y.end(), std::less<T>{}, std::equal_to<T>{});

这将导致四个范围:

  • x1:[x.begin(), xm)
  • x2:[xm, x.end())
  • y1:[y.begin(), ym)
  • y2:[ym, y.end())

x1y1进行排序并包含等效元素的位置(根据二进制谓词p)。在示例中,x1y1有效地包含了xy的排序交集。 x2y2也已排序,并分别包含xy唯一的所有元素。

我是否错过了通过结合STL中的现有算法来实现这种算法的明显方法?我现在打算为此编写一个自定义实现,但是想先在这里进行检查,然后再开始实现。该算法的好名字是什么?

更新,我已经实现了满足我要求的以下算法。该算法要求对输入范围进行排序。

/// Takes two sorted input ranges, and partitions both ranges such that all
/// elements that occur in both ranges precede those elements that only occur in
/// one of the two ranges.
template<typename ForwardIt1, typename ForwardIt2, typename Compare>
std::pair<ForwardIt1, ForwardIt2> frc::binary_partition(
    ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2, ForwardIt2 last2, Compare comp)
{
  auto equals = [&](const auto& x, const auto& y) { return !comp(x, y) && !comp(y, x); };

  // Invariant: first1 and last1 point to the first mismatch.
  std::tie(first1, first2) = std::mismatch(first1, last1, first2, last2, equals);

  while (first1 != last1 && first2 != last2)
  {
    // Iterators to the next matching elements.
    auto fm1{first1};
    auto fm2{first2};

    // Find next matching elements in both ranges.
    while (comp(*fm1, *fm2) || comp(*fm2, *fm1))
    {
      if (comp(*fm1, *fm2))
      {
        ++fm1;
      }
      else
      {
        ++fm2;
      }

      if (fm1 == last1 || fm2 == last2)
      {
        return std::pair(first1, first2);
      }
    }

    // Find the end of the matching subsequence.
    auto [lm1, lm2] = std::mismatch(fm1 + 1, last1, fm2 + 1, last2, equals);

    // In case matching elements are found, move the mismatching subrange behind
    // the matching subrange.
    first1 = std::rotate(first1, fm1, lm1);
    first2 = std::rotate(first2, fm2, lm2);
  }

  return std::pair(first1, first2);
}

3 个答案:

答案 0 :(得分:0)

不,我可以想到两个原因:

  1. 您实际上需要两种算法:std::sortstd::stable_partition(或每个范围的两个分区的std::partitionstd::sort)。您可以自己撰写。

  2. 算法以迭代器的方式实现其语义-在此处应实现使它与两个范围一起使用的多余部分。换句话说,如果一个算法的语义可以在一个输入范围内定义,那么多个输入就不会有重载。

    您必须使用一个自定义的迭代器,它将在内部操纵两个不同范围的迭代器(容器)。我认为boost::zip_iterator是您想要的。

答案 1 :(得分:0)

据我所知,STL中没有此类功能。

也许您可以做的是利用STL提供的以下功能:

  • 对x
  • 进行排序
  • 排序y
  • 在x和y上使用mismatch和二进制谓词

它返回一对迭代器,指向两个序列中第一个不匹配的元素。

template <class InputIterator1, class InputIterator2, class BinaryPredicate>
  pair<InputIterator1, InputIterator2>
    mismatch (InputIterator1 first1, InputIterator1 last1,
              InputIterator2 first2, BinaryPredicate pred);

答案 2 :(得分:0)

在我们可以从p合成comp的情况下,这大概是

template<typename ForwardIt1, typename ForwardIt2, typename Compare>
std::pair<ForwardIt1, ForwardIt2> sort_and_partition(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2, ForwardIt2 last2, Compare comp)
{
    // We don't strictly need copies of the two ranges, but it makes things easier. 
    // We do want them sorted, using an ordered set here gives that for free
    std::multiset<typename std::iterator_traits<ForwardIt1>::value_type, Compare> set1{ first1, last1, comp };
    std::multiset<typename std::iterator_traits<ForwardIt2>::value_type, Compare> set2{ first2, last2, comp };

    // Copy back into a partitioned [first1, last1)
    auto mid1 = std::set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(), first1, comp);
    std::set_difference(set1.begin(), set1.end(), set2.begin(), set2.end(), mid1, comp);

    // Copy back into a partitioned [first2, last2)
    auto mid2 = std::set_intersection(set2.begin(), set2.end(), set1.begin(), set1.end(), first2, comp);
    std::set_difference(set2.begin(), set2.end(), set1.begin(), set1.end(), mid2, comp);

    // And return our midpoints
    return { mid1, mid2 };
}

在更一般的情况下,您将需要确定在分区步骤中要比较多少对元素,因为不同的选择将导致不同的结果。