简体   繁体   中英

std::set::equal_range for a container of std::pair

I'm trying to find all the ranges [a,b] enclosing int values i, where a <= i <= b. I'm using set<std:pair<int,int>> for the set of ranges.

In the following, using equal range on a vector<int> yields the start and one past the end of the range.

When I do the same for a set<pair<int,int>> , the result starts and ends at one past the end of the range and therefore doesn't include the range enclosing the value.

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

int main()
{
    int ia[] = {1,2,3,4,5,6,7,8,9,10};
    set<int> s1(begin(ia),end(ia));
    auto range1 = s1.equal_range(5);

    cout << *range1.first << " " << *range1.second << endl; //prints 5 6

    pair<int,int> p[] = {make_pair(1,10), 
                         make_pair(11,20),  
                         make_pair(21,30), 
                         make_pair(31,40)}; 
    set<pair<int,int>> s(begin(p), end(p));    

    auto range = s.equal_range(make_pair(12,12));

    cout << range.first->first << " " << range.first->second << endl; //prints 21 30, why?
    cout << range.second->first << " " << range.second->second << endl; //prints 21 30

}

prints
5 6
21 30
21 30
Why does equal_range on the set<pair<int,int>> not include the range that encloses the value (12), namely [11.20]

equal_range is behaving completely correctly:

assert( std::make_pair(11, 20) < std::make_pair(12, 12) );
assert( std::make_pair(12, 12) < std::make_pair(21, 30) );

[11,20] is not a range, it's a pair. The pair [12,12] is not "within" another pair, that makes no sense to even say.

[12,12] is not "within" [11,20], it's greater than it. The less-than operator for std::pair compares the first elements first, and only if they're equal does it look at the second elements, so make_pair(11,x) is less than make_pair(12, y) for any x and y

So equal_range tells you that [12,12] would be inserted after [11,20] and before [21,30], which is correct.

If you want to treat pairs as ranges of values you need to write code to do that, not assume the built-in comparisons for pairs does that. You're actually trying to find an int 12 in a range of pairs of ints, but have written code to find a pair [12,12] in a range of pairs of ints. That's not the same thing.

It does not include [11, 20] in the range, because it doesn't include anything in the range. There are no equal elements to [12, 12] , so it returns an empty range (represented by the half-open interval [x, x) ).

BTW dereferencing the upper bound of the range may invoke undefined behavior, since that may be equal to s.end() .

The pair [12, 12] is sorted after [11, 20] and before [21, 30] .

std::set::equal_range() includes a range of equal elements. There is no equal element in your set (especially not [11, 20] ), so equal_range() returns [21, 30], [21, 30] .

equal_range is implemented as to call lower_bound first then call upper_bound to search the rest data set.

template <class ForwardIterator, class T>
  pair<ForwardIterator,ForwardIterator>
    equal_range ( ForwardIterator first, ForwardIterator last, const T& value )
{
  ForwardIterator it = lower_bound (first,last,value);
  return make_pair ( it, upper_bound(it,last,value) );
}

Look at your sample: It calls lower_bound to locate the lower bound of value(which is pair(12,12), which arrives at

pair<int,int> p[] = {make_pair(1,10), 
                     make_pair(11,20), 
                     make_pair(21,30),   // <--- lower_bound() points to here
                     make_pair(31,40)}; 

Then it calls upper_bound() to search on (21,30),(31,40) and it cound't find it, it returns (21,30)

http://www.cplusplus.com/reference/algorithm/upper_bound/

I don't think your std::set<std::pair<int, int> > won't help you much in intersecting it with your integer: You can find the s.lower_bound(std::make_pair(i + 1, i + 1) to cut off the search but all ranges starting at an index lower than i + 1 can potentially include the value i if the second boundary is large enough. What might help is if you know the maximum size of the ranges in which case you can bound the search towards the front by s.lower_bound(std::make_pair(i - max_range_size, i - max_range_size)) . You'll need to inspect each of the ranges in turn to identify if your i falls into them:

auto it    = s.lower_bound(std::make_pair(i - max_range_size, i - max_range_size));
auto upper = s.lower_bound(std::make_pair(i + 1, i + 1));
for (; it != upper; ++it) {
    if (i < it->second) {
        std::cout << "range includes " << i << ": "
                  << [" << it.first << ", " << it->second << "]\n";
}

(or something like this...)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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