Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to compute the size of an intersection of two STL sets in C++

Tags:

c++

algorithm

stl

I am given two sets (std::set from <set>) of which I'd like to know the size of the intersection. I could use std::set_intersection from <algorithm>, but I have to provide it an output iterator to copy the intersection into some other container.

A straightforward way would be

  set<int> s1{1,2,3,4,5};
  set<int> s2{4,5,6,7,8,9,0,1};

  vector<int> v;

  set_intersection(
      s1.begin(), s1.end(), s2.begin(), s2.end(),
      inserter(v, v.begin()));

after which v.size() gives the size of the intersection. However, the intersection will have to be stored as well, even though we don't do anything with it.

To avoid that, I tried to implement a dummy output iterator class, which only counts, but it doesn't assign:

template<typename T>
class CountingOutputIterator {
 private:
  int* counter_;
  T dummy_;
 public:
  explicit CountingOutputIterator(int* counter) :counter_(counter) {}
  T& operator*() {return dummy_;}
  CountingOutputIterator& operator++() { // ++t
    (*counter_)++;
    return *this;
  }
  CountingOutputIterator operator++(int) { // t++
    CountingOutputIterator ret(*this);
    (*counter_)++;
    return ret;
  }
  bool operator==(const CountingOutputIterator& c) {
    return counter_ == c.counter_; // same pointer
  }
  bool operator!=(const CountingOutputIterator& c) {
    return !operator==(c);
  }
};

using which we could do

  set<int> s1{1,2,3,4,5};
  set<int> s2{4,5,6,7,8,9,0,1};

  int counter = 0;
  CountingOutputIterator<int> counter_it(&counter);
  set_intersection(
      s1.begin(), s1.end(), s2.begin(), s2.end(), counter_it);

after which counter holds the size of the intersection.

This is much more code however. My questions are:

1) Is there a standard (library) way or a standard trick to obtain the size of the intersection without storing the whole intersection? 2) Independent of whether or not there is, is the approach with the custom dummy iterator a good one?

like image 414
doetoe Avatar asked Sep 17 '15 21:09

doetoe


People also ask

What does size() function return?

size() function is used to return the size of the set container or the number of elements in the set container. Return Value: It returns the number of elements in the set container.

How do you find the intersection of two sets in CPP?

C++ Algorithm set_intersection() function is used to find the intersection of two sorted ranges[first1, last1) and [first2, last2), which is formed only by the elements that are present in both sets.

How to take intersection of two strings in c++?

std::set_intersection in C++ The intersection of two sets is formed only by the elements that are present in both sets. The elements copied by the function come always from the first range, in the same order. The elements in the both the ranges shall already be ordered.


2 Answers

It's not difficult to write a loop that moves through the two sets looking for matching elements, or you could do this, which is much simpler than a custom iterator:

struct Counter
{
  struct value_type { template<typename T> value_type(const T&) { } };
  void push_back(const value_type&) { ++count; }
  size_t count = 0;
};

template<typename T1, typename T2>
size_t intersection_size(const T1& s1, const T2& s2)
{
  Counter c;
  set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(), std::back_inserter(c));
  return c.count;
}
like image 71
Jonathan Wakely Avatar answered Nov 15 '22 22:11

Jonathan Wakely


You could do this:

auto common = count_if(begin(s1), end(s1), [&](const auto& x){ return s2.find(x) != end(s2); });

It's not optimally efficient but should be fast enough for most purposes.

like image 24
mattnewport Avatar answered Nov 15 '22 22:11

mattnewport