我想知道以下算法的标准方法是否存在。我想对两个范围x
和y
进行排序和分区。 x
和y
都应使用二进制谓词进行分区,该谓词采用x
和y
中的元素。该函数的签名应类似于:
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>{});
这将导致四个范围:
[x.begin(), xm)
[xm, x.end())
[y.begin(), ym)
[ym, y.end())
对x1
和y1
进行排序并包含等效元素的位置(根据二进制谓词p
)。在示例中,x1
和y1
有效地包含了x
和y
的排序交集。 x2
和y2
也已排序,并分别包含x
和y
唯一的所有元素。
我是否错过了通过结合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);
}
答案 0 :(得分:0)
不,我可以想到两个原因:
您实际上需要两种算法:std::sort
和std::stable_partition
(或每个范围的两个分区的std::partition
和std::sort
)。您可以自己撰写。
算法以迭代器的方式实现其语义-在此处应实现使它与两个范围一起使用的多余部分。换句话说,如果一个算法的语义可以在一个输入范围内定义,那么多个输入就不会有重载。
您必须使用一个自定义的迭代器,它将在内部操纵两个不同范围的迭代器(容器)。我认为boost::zip_iterator
是您想要的。
答案 1 :(得分:0)
据我所知,STL中没有此类功能。
也许您可以做的是利用STL提供的以下功能:
它返回一对迭代器,指向两个序列中第一个不匹配的元素。
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 };
}
在更一般的情况下,您将需要确定在分区步骤中要比较多少对元素,因为不同的选择将导致不同的结果。