合并集合中满足谓词的元素

时间:2020-04-29 14:46:20

标签: c++ algorithm

我想知道是否有一种算法可以合并满足谓词的集合中的元素 类似于以下代码:

struct Element
{
  int size;
  constexpr static int maxSize = 150;
  enum Type { Mergeable, notMergeable } type;
};

auto predicate = [](const Element& el1, const Element& el2)
{
  //Done by the algorithm

  if(&el1 == &el2) // An element should not merge with itself obviously
    return false;

  if(el1.size == 0 || el2.size == 0) //element is marked for deletion
    return false; 

  //User predicate:
  bool isBelowMaxSize = el1.size + el2.size < Element::maxSize;
  bool isMergeable = el1.type  == Element::Type::Mergeable  && el2.type  == Element::Type::Mergeable;

  return isBelowMaxSize && isMergeable;
}

//User merge function
auto merge = [](Element& el1, Element& el2)
{
  el1 += el2;

  //Done by the algorithm
  el2.size = 0; //Marks for deletion
}

int main()
  std::vector<Element> els;
  //Let's assume els contains elements
  for(auto& el1 : els)
      for(auto& el2 : els)
          if(predicate(el1, el2))
            merge(el1, el2)

  //Merged elements are now removed
}

我以为我可以对范围做同样的事情:

    namespace rv = ranges::views;
    auto result = rv::cartesian_product(els, els) | rv::filter(predicate) | rv::for_each(merge);

但是我担心它无法正常工作,因为它可能会尝试合并已经合并的元素。

那么,有没有一种干净的方法?

2 个答案:

答案 0 :(得分:2)

您可以通过首先将所有Element::Type::Mergeablesize < Element::maxSize的{​​{1}}项过滤到一个单独的容器中(这是 O(n)),然后按大小排序( O(n log n))。

有了分类的候选容器,您可以轻松地从两端进行遍历,直到迭代器在中间相遇为止。组合最大和最小元素将在线性时间内给出所有允许的合并。

答案 1 :(得分:0)

感谢@ Useless,@ Ted Lyngmo和@Caleth的帮助,这是我的答案

//Expects a range sorted in descending order
template<class It, class Predicate, class Sum>
[[nodiscard]] static It merge_if(It first, It last, Predicate predicate, Sum sum)
{
    assert(first != last);
    last--;

    while (first != last)
    {
        while (predicate(*first, *last))
        {
            *first = sum(*first, *last);
            last--;
        }
        first++;
    }
    first++;
    return first; //One past the end
}

template<class It, class Predicate>
[[nodiscard]] static It merge_if(It first, It last, Predicate predicate)
{
    using Type = std::remove_cv_t<std::remove_reference_t<decltype(*first)>>;
    auto plus = [](const Type &first, const Type &second) { return first + second; };
    return merge_if(first, last, predicate, plus);
}

my GitLab上有一些测试用例