同时使用set_difference和set_intersection

7
我想知道标准库中是否有任何工具可以同时计算两个已排序范围之间的集合交集和差集。希望它的签名类似于以下形式:
template <class Input1, class Input2, 
          class Output1, class Output2, class Output3>
Output3 decompose_sets (Input1 first1, Input1 last1,
                        Input2 first2, Input2 last2,
                        Output1 result1, Output2 result2,
                        Output3 result3 );

经过调用decompose sets后,result1包含[first1,last1)中所有不在[first2,last2)中的元素,result2包含[first2,last2)中所有不在[first1,last1)中的元素,result3包含[first1,last1)[first2,last2)中共有的元素。

cplusplus.com上set_differenceset_intersection的示例实现似乎可以帮助我创建一个高效的实现,只需要进行一次扫描,而不是三次。但如果它已经在标准库中了,我就不想重复造轮子了。

例如,如请求所示:

给定两个集合a={0, 1, 2, 3, 4}和b={2, 4, 5, 6},那么我想构建以下三个集合:

  • only_a = {0,1,3}
  • only_b = {5,6}
  • common = {2,4}

为什么是 [first1,last2) - P0W
你能举个例子说明你想要实现什么吗?例如,使用集合 {0, 1, 2, 3} 和 {0, 2, 4, 6}。 - cpp
@GrzegorzWilanowski 在问题中添加了示例。 - cheshirekow
请注意结果应该是 std::tuple<Output1, Output2, Output3>,否则您可以只使用 Output1Output2 的插入器迭代器。 - Jarod42
3个回答

3

没有标准库算法可以在一次扫描中完成,但很容易编写。以下内容看起来正确,输出也有意义在ideone.com这里

template <class Input1, class Input2,
            class Output1, class Output2, class Output3>
Output3 decompose_sets(Input1 first1, Input1 last1,
                    Input2 first2, Input2 last2,
                    Output1 result1, Output2 result2,
                    Output3 result3)
{
    while (first1 != last1 && first2 != last2) {
        if (*first1 < *first2) {
            *result1++ = *first1++;
        } else if (*first2 < *first1) {
            *result2++ = *first2++;
        } else {
            *result3++ = *first1++;
            ++first2; // skip common value in set2
        }
    }
    std::copy(first1, last1, result1);
    std::copy(first2, last2, result2);
    return result3;
}

这是我也写的一份准确的副本。但如果它不在标准库中,那就回答了我的问题。所以谢谢。另外,感谢您教我ideaone.com。那是一个方便的网站。 - cheshirekow
@cheshirekow:不用谢。我也修正了我的ideone.com的拼写(想一想“IDE One”)。 - Blastfurnace

1

在STL中没有这样的函数,但是有set_symmetric_difference(),它可以构建一个排序序列,其中包含第一个序列中存在但第二个序列中不存在的元素以及第二个序列中存在但第一个序列中不存在的元素。


1
是的,但set_symmetric_difference不会将它们分开成a_onlyb_only - cheshirekow
我们只能希望他们会添加一个重载函数,它将接受第二个目标迭代器,以便从两个列表中的差异被单独存储,而不是在合并结果中存储。 - user362515

0

这里有另一种选择,即使用回调函数以实现最大的灵活性。

template <class Input1, class Input2
            , class FuncAdd, class FuncRm, class FuncSame, class Comp>
void set_difference_adv(Input1 firstOld, Input1 lastOld
                        ,Input2 firstNew, Input2 lastNew
                        ,FuncAdd onadded, FuncRm onremoved, FuncSame onsame, Comp comp)
{
  while (firstOld != lastOld && firstNew != lastNew) {

    if (comp(*firstOld, *firstNew)) {
      onremoved(*firstOld++);
    } else if (comp(*firstNew, *firstOld)) {
      onadded(*firstNew++);
    } else {
      onsame(*firstOld++, *firstNew++);
    }
  }

  std::for_each(firstOld, lastOld, onremoved);
  std::for_each(firstNew, lastNew, onadded);
}

这样做有以下优点:

  • 现在输出列表是可选的
  • 输出可以是不同类型(转换)
  • 共同项可以并行进一步处理(附加比较)

“现实世界”例子:

int main()
{
  using File = std::pair<std::string, int>;

  std::vector<File> files1{{"file1", 12}, {"file3", 8}, {"file4", 2}, {"file5", 10}};
  std::vector<File> files2{{"file1", 12}, {"file2", 5}, {"file3", 8}, {"file4", 33}};

  const auto less = [](const auto& o, const auto& n) { return o.first < n.first; };

  std::vector<std::string> addedNames;
  std::vector<File> changedFiles;

  set_difference_adv(std::cbegin(files1), std::cend(files1)
                     ,std::cbegin(files2), std::cend(files2)
                     , [&addedNames](const auto& val){ addedNames.push_back(val.first); } //< added (transform)
                     , [](const auto& val) {} //< removed (ignore)
                     , [&changedFiles](const auto& o, const auto& n){ if(n.second > o.second) changedFiles.push_back(n); } //< "same" (further compare)
                     , less
                     );  
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接