Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find all the numbers in the range [a, b] that are not in the given std::set S

Let a and b be integers, a < b. Given an std::set<int> S what is an efficient and elegant (preferably without explicit loops) way to find and store (into a vector) all the numbers from [a, b] that are not in S.

Solution 1:

 vector<int> v;
 for(int i = a; i <= b; ++i)
 {
     if(S.find(i) == S.end())
     {
        v.push_back(i);
     }         
}

Solution 2:

Push all the numbers from a to b into a set and use std::set_difference

Solution1 contains an explicit loop, and solution2 does not seem very efficient (at least in terms of memory). What would you suggest? I am looking for an elegant STL-ish (boost is also acceptible) idiomatic way to do this.

like image 523
Armen Tsirunyan Avatar asked May 17 '12 12:05

Armen Tsirunyan


People also ask

How do you define a range in C++?

Use the range-based for statement to construct loops that must execute through a range, which is defined as anything that you can iterate through—for example, std::vector , or any other C++ Standard Library sequence whose range is defined by a begin() and end() .

How do you check if a set contains an element in C++?

C++ set find() C++ set find() function is used to find an element with the given value val. If it finds the element then it returns an iterator pointing to the element otherwise, it returns an iterator pointing to the end of the set i.e. set::end().

Is std::set ordered?

Yes, std::set stores its elements in such a way that iterating over the elements will be done in sorted order (and the call to std::adjacent_find is to show that std::set stores unique items as well).

What is the complexity of different std::set operations?

std::set is commonly implemented as a red-black binary search tree. Insertion on this data structure has a worst-case of O(log(n)) complexity, as the tree is kept balanced.


1 Answers

You can do something like your solution #2. But instead of creating an actual container containing the range [a,b], use boost::irange, which is a virtual container for a numeric range. This way you have no explicit loops, it will run in linear time, and not take too much memory.

To make it even faster, make it cover only the relevant part of the set by using lower_bound/upper_bound:

auto abRange = boost::irange(a,b+1);
std::set_difference(abRange.begin(), abRange.end(), 
                    s.lower_bound(a), s.upper_bound(b), 
                    std::back_inserter(resultVector));

Or using Boost.Range's set_difference:

boost::set_difference(boost::irange(a,b+1),
                      std::make_pair(s.lower_bound(a), s.upper_bound(b)),
                      std::back_inserter(resultVector));
like image 137
interjay Avatar answered Oct 23 '22 20:10

interjay