简体   繁体   English

如何在 C++ 中找到两个 std::set 的交集?

[英]how to find the intersection of two std::set in C++?

I have been trying to find the intersection between two std::set in C++, but I keep getting an error.我一直试图在 C++ 中找到两个 std::set 之间的交集,但我一直收到错误消息。

I created a small sample test for this我为此创建了一个小样本测试

#include <iostream>
#include <vector>
#include <algorithm>
#include <set>
using namespace std;

int main() {
  set<int> s1;
  set<int> s2;

  s1.insert(1);
  s1.insert(2);
  s1.insert(3);
  s1.insert(4);

  s2.insert(1);
  s2.insert(6);
  s2.insert(3);
  s2.insert(0);

  set_intersection(s1.begin(),s1.end(),s2.begin(),s2.end());
  return 0;
}

The latter program does not generate any output, but I expect to have a new set (let's call it s3 ) with the following values:后一个程序不生成任何输出,但我希望有一个具有以下值的新集合(我们称之为s3 ):

s3 = [ 1 , 3 ]

Instead I'm getting the error:相反,我收到了错误:

test.cpp: In function ‘int main()’:
test.cpp:19: error: no matching function for call to ‘set_intersection(std::_Rb_tree_const_iterator<int>, std::_Rb_tree_const_iterator<int>, std::_Rb_tree_const_iterator<int>, std::_Rb_tree_const_iterator<int>)’

What I understand out of this error, is that there's no definition in set_intersection that accepts Rb_tree_const_iterator<int> as parameter.我从这个错误中set_intersectionRb_tree_const_iterator<int>没有接受Rb_tree_const_iterator<int>作为参数的定义。

Furthermore, I suppose the std::set.begin() method returns an object of such type,此外,我想std::set.begin()方法返回这种类型的对象,

is there a better way to find the intersection of two std::set in C++?有没有更好的方法可以在 C++ 中找到两个std::set的交集? Preferably a built-in function?最好是内置函数?

Thanks a lot!非常感谢!

You haven't provided an output iterator for set_intersection您尚未为set_intersection提供输出迭代器

template <class InputIterator1, class InputIterator2, class OutputIterator>
OutputIterator set_intersection ( InputIterator1 first1, InputIterator1 last1,
                                  InputIterator2 first2, InputIterator2 last2,
                                  OutputIterator result );

Fix this by doing something like通过做类似的事情来解决这个问题

...;
set<int> intersect;
set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(),
                 std::inserter(intersect, intersect.begin()));

You need a std::insert iterator since the set is as of now empty.您需要一个std::insert迭代器,因为该集合现在是空的。 We cannot use std::back_inserter or std::front_inserter since set doesn't support those operations.我们不能使用std::back_inserterstd::front_inserter因为 set 不支持这些操作。

Have a look at the sample in the link: http://en.cppreference.com/w/cpp/algorithm/set_intersection查看链接中的示例: http : //en.cppreference.com/w/cpp/algorithm/set_intersection

You need another container to store the intersection data, below code suppose to work:您需要另一个容器来存储交叉数据,下面的代码假设可以工作:

std::vector<int> common_data;
set_intersection(s1.begin(),s1.end(),s2.begin(),s2.end(), std::back_inserter(common_data));

See std::set_intersection .std::set_intersection You must add an output iterator, where you will store the result:您必须添加一个输出迭代器,您将在其中存储结果:

#include <iterator>
std::vector<int> s3;
set_intersection(s1.begin(),s1.end(),s2.begin(),s2.end(), std::back_inserter(s3));

See Ideone for full listing.有关完整列表,请参阅Ideone

The first (well-voted) comment of the accepted answer complains about a missing operator for the existing std set operations.接受答案的第一个(投票得当)评论抱怨现有标准集操作缺少运算符。

On one hand, I understand the lack of such operators in the standard library.一方面,我理解标准库中缺少这样的操作符。 On the other hand, it is easy to add them (for the personal joy) if desired.另一方面,如果需要,很容易添加它们(为了个人乐趣)。 I overloaded我超载了

  • operator *() for intersection of sets operator *()用于集合的交集
  • operator +() for union of sets. operator +()用于集合的并集。

Sample test-set-ops.cc :示例test-set-ops.cc

#include <algorithm>
#include <iterator>
#include <set>

template <class T, class CMP = std::less<T>, class ALLOC = std::allocator<T> >
std::set<T, CMP, ALLOC> operator * (
  const std::set<T, CMP, ALLOC> &s1, const std::set<T, CMP, ALLOC> &s2)
{
  std::set<T, CMP, ALLOC> s;
  std::set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(),
    std::inserter(s, s.begin()));
  return s;
}

template <class T, class CMP = std::less<T>, class ALLOC = std::allocator<T> >
std::set<T, CMP, ALLOC> operator + (
  const std::set<T, CMP, ALLOC> &s1, const std::set<T, CMP, ALLOC> &s2)
{
  std::set<T, CMP, ALLOC> s;
  std::set_union(s1.begin(), s1.end(), s2.begin(), s2.end(),
    std::inserter(s, s.begin()));
  return s;
}

// sample code to check them out:

#include <iostream>

using namespace std;

template <class T>
ostream& operator << (ostream &out, const set<T> &values)
{
  const char *sep = " ";
  for (const T &value : values) {
    out << sep << value; sep = ", ";
  }
  return out;
}

int main()
{
  set<int> s1 { 1, 2, 3, 4 };
  cout << "s1: {" << s1 << " }" << endl;
  set<int> s2 { 0, 1, 3, 6 };
  cout << "s2: {" << s2 << " }" << endl;
  cout << "I: {" << s1 * s2 << " }" << endl;
  cout << "U: {" << s1 + s2 << " }" << endl;
  return 0;
}

Compiled and tested:编译和测试:

$ g++ -std=c++11 -o test-set-ops test-set-ops.cc 

$ ./test-set-ops     
s1: { 1, 2, 3, 4 }
s2: { 0, 1, 3, 6 }
I: { 1, 3 }
U: { 0, 1, 2, 3, 4, 6 }

$ 

What I don't like is the copy of return values in the operators.我不喜欢的是运算符中返回值的副本。 May be, this could be solved using move assignment but this is still beyond my skills.也许,这可以使用移动分配来解决,但这仍然超出了我的技能。

Due to my limited knowledge about these "new fancy" move semantics, I was concerned about the operator returns which might cause copies of the returned sets.由于我对这些“新奇”移动语义的了解有限,我担心可能会导致返回集合的副本的运算符返回。 Olaf Dietsche pointed out that these concerns are unnecessary as std::set is already equipped with move constructor/assignment. Olaf Dietsche指出这些担忧是不必要的,因为std::set已经配备了移动构造函数/赋值。

Although I believed him, I was thinking how to check this out (for something like "self-convincing").虽然我相信他,但我正在考虑如何检查这一点(例如“自我说服”之类的东西)。 Actually, it is quite easy.实际上,这很容易。 As templates has to be provided in source code, you can simply step through with the debugger.由于模板必须在源代码中提供,您可以简单地使用调试器逐步完成。 Thus, I placed a break point right at the return s;因此,我在return s;处放置了一个断点return s; of the operator *() and proceeded with single-step which leaded me immediately into std::set::set(_myt&& _Right) : et voilà – the move constructor. operator *()并继续单步执行,这使我立即进入std::set::set(_myt&& _Right) :et voilà – 移动构造函数。 Thanks, Olaf, for the (my) enlightment.谢谢,奥拉夫,为(我的)启蒙。

For the sake of completeness, I implemented the corresponding assignment operators as well为了完整起见,我也实现了相应的赋值运算符

  • operator *=() for "destructive" intersection of sets operator *=()用于“破坏性”集合的交集
  • operator +=() for "destructive" union of sets. operator +=()用于“破坏性”集合并集。

Sample test-set-assign-ops.cc :示例test-set-assign-ops.cc

#include <iterator>
#include <set>

template <class T, class CMP = std::less<T>, class ALLOC = std::allocator<T> >
std::set<T, CMP, ALLOC>& operator *= (
  std::set<T, CMP, ALLOC> &s1, const std::set<T, CMP, ALLOC> &s2)
{
  auto iter1 = s1.begin();
  for (auto iter2 = s2.begin(); iter1 != s1.end() && iter2 != s2.end();) {
    if (*iter1 < *iter2) iter1 = s1.erase(iter1);
    else {
      if (!(*iter2 < *iter1)) ++iter1;
      ++iter2;
    }
  }
  while (iter1 != s1.end()) iter1 = s1.erase(iter1);
  return s1;
}

template <class T, class CMP = std::less<T>, class ALLOC = std::allocator<T> >
std::set<T, CMP, ALLOC>& operator += (
  std::set<T, CMP, ALLOC> &s1, const std::set<T, CMP, ALLOC> &s2)
{
  s1.insert(s2.begin(), s2.end());
  return s1;
}

// sample code to check them out:

#include <iostream>

using namespace std;

template <class T>
ostream& operator << (ostream &out, const set<T> &values)
{
  const char *sep = " ";
  for (const T &value : values) {
    out << sep << value; sep = ", ";
  }
  return out;
}

int main()
{
  set<int> s1 { 1, 2, 3, 4 };
  cout << "s1: {" << s1 << " }" << endl;
  set<int> s2 { 0, 1, 3, 6 };
  cout << "s2: {" << s2 << " }" << endl;
  set<int> s1I = s1;
  s1I *= s2;
  cout << "s1I: {" << s1I << " }" << endl;
  set<int> s2I = s2;
  s2I *= s1;
  cout << "s2I: {" << s2I << " }" << endl;
  set<int> s1U = s1;
  s1U += s2;
  cout << "s1U: {" << s1U << " }" << endl;
  set<int> s2U = s2;
  s2U += s1;
  cout << "s2U: {" << s2U << " }" << endl;
  return 0;
}

Compiled and tested:编译和测试:

$ g++ -std=c++11 -o test-set-assign-ops test-set-assign-ops.cc 

$ ./test-set-assign-ops
s1: { 1, 2, 3, 4 }
s2: { 0, 1, 3, 6 }
s1I: { 1, 3 }
s2I: { 1, 3 }
s1U: { 0, 1, 2, 3, 4, 6 }
s2U: { 0, 1, 2, 3, 4, 6 }

$

Just comment here.只是在这里评论。 I think it is time to add union, intersect operation to the set interface.我觉得是时候给set接口添加union、intersect操作了。 Let's propose this in the future standards.让我们在未来的标准中提出这个建议。 I have been using the std for a long time, each time I used the set operation I wished the std were better.我用std很久了,每次用set操作都希望std好点。 For some complicated set operation, like intersect, you may simply (easier?) modify the following code:对于一些复杂的集合操作,比如相交,你可以简单地(更容易?)修改以下代码:

template <class InputIterator1, class InputIterator2, class OutputIterator>
  OutputIterator set_intersection (InputIterator1 first1, InputIterator1 last1,
                                   InputIterator2 first2, InputIterator2 last2,
                                   OutputIterator result)
{
  while (first1!=last1 && first2!=last2)
  {
    if (*first1<*first2) ++first1;
    else if (*first2<*first1) ++first2;
    else {
      *result = *first1;
      ++result; ++first1; ++first2;
    }
  }
  return result;
}

copied from http://www.cplusplus.com/reference/algorithm/set_intersection/复制自http://www.cplusplus.com/reference/algorithm/set_intersection/

For example, if your output is a set, you can output.insert(*first1).例如,如果您的输出是一个集合,则可以 output.insert(*first1)。 Furthermore, you function may not be templated.If you code can be shorter than using the std set_intersection function then go ahead with it.此外,您的函数可能没有被模板化。如果您的代码可以比使用 std set_intersection 函数更短,那么继续使用它。

If you want to do a union of two set you can simply setA.insert(setB.begin(), setB.end());如果你想做两个集合的并集,你可以简单地 setA.insert(setB.begin(), setB.end()); This is much simpler than the set_union method.这比 set_union 方法简单得多。 However, this will not work with vector.但是,这不适用于矢量。

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

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