tags:

views:

181

answers:

2

The standard way of intersecting two sets in C++ is to do the following:

std::set<int> set_1;  // With some elements
std::set<int> set_2;  // With some other elements
std::set<int> the_intersection;  // Destination of intersect
std::set_intersection(set_1.begin(), set_1.end(), set_2.begin(), set_2.end(), std::inserter(the_intersection, the_intersection.end());

How would I go about doing an in-place set intersection? That is, I want set_1 to have the results of the call to set_intersection. Obviously, I can just do a set_1.swap(the_intersection), but this is a lot less efficient than intersecting in-place.

+3  A: 

You can easily go through set_1, check each element to see if it exists in set_2, and erase it if it doesn't. Since sets are sorted, you can compare them in linear time, but I'm not sure about the overhead of erasing an element. I wouldn't count on it being more efficient than what you started with.

Mark Ransom
True enough on all points. It seems to me intuitively that it should be possible to iterate through both sets at once and do the intersection in place. I just don't immediately see how.
ChrisInEdmonton
+4  A: 

I think I've got it:

std::set<int>::iterator it1 = set_1.begin();
std::set<int>::iterator it2 = set_2.begin();
while ( (it1 != set_1.end()) && (it2 != set_2.end()) ) {
    if (*it1 < *it2) {
     set_1.erase(it1++);
    } else if (*it2 < *it1) {
     ++it2;
    } else { // *it1 == *it2
            ++it1;
            ++it2;
    }
}
// Anything left in set_1 from here on did not appear in set_2,
// so we remove it.
set_1.erase(it1, set_1.end());

Anyone see any problems? Seems to be O(n) on the size of the two sets. According to cplusplus.com, std::set erase(position) is amortized constant while erase(first,last) is O(log n).

ChrisInEdmonton
The continues are redundant, and I'd rearrange to be `if(*it1<*it2) else if(*it2<*it1) else ...` so that the only comparison operator you're using is less than - that's how `set` works.
Mark Ransom
Right! Because it's if-else if, etc. I was thinking the following conditionals would be checked. Thanks, I'll edit the answer.
ChrisInEdmonton
`set_1.erase(it1++)` is incorrect for some containers (such as vector), even if it is valid in your case. You should use `it1 = set_1.erase(it1)` that is valid with all containers.
David Rodríguez - dribeas
Would it1 be incremented properly in that case? That is, does set_1.erase(it1) increment the iterator?
ChrisInEdmonton
`set::erase` doesn't increment the iterator, but many implementations give you the incremented iterator as a return value. I don't think it was part of the original C++ spec though, see for example http://www.cplusplus.com/reference/stl/set/erase/
Mark Ransom