STL provides binary search functions std::lower_bound and std::upper_bound, but I tend not to use them because I've been unable to remember what they do, because their contracts seem completely mystifying to me.
Just from looking at the names,
I'd guess that "lower_bound" might be short for "last lower bound",
i.e. the last element in the sorted list that is <= the given val (if any).
And similarly I'd guess "upper_bound" might be short for "first upper bound",
i.e. the first element in the sorted list that is >= the given val (if any).
But the documentation says they do something rather different from that--
something that seems to be a mixture of backwards and random, to me.
To paraphrase the doc:
- lower_bound finds the first element that's >= val
- upper_bound finds the first element that's > val
So lower_bound doesn't find a lower bound at all; it finds the first upper bound!? And upper_bound finds the first strict upper bound.
Does this make any sense?? How do you remember it?
Consider the sequence
lower bound for 6 is the position of the first 6.
upper bound for 6 is the position of the 7.
these positions serve as common (begin, end) pair designating the run of 6-values.
Example:
In this case, I think a picture is worth a thousand words. Let's assume we use them to search for
2
in the following collections. The arrows show what iterators the two would return:So, if you have more than one object with that value already present in the collection,
lower_bound
will give you an iterator that refers to the first one of them, andupper_bound
will give an iterator that refers to the object immediately after the last one of them.This (among other things) makes the returned iterators usable as the
hint
parameter toinsert
.Therefore, if you use these as the hint, the item you insert will become the new first item with that value (if you used
lower_bound
) or last item with that value (if you usedupper_bound
). If the collection didn't contain an item with that value previously, you'll still get an iterator that can be used as ahint
to insert it in the correct position in the collection.Of course, you can also insert without a hint, but using a hint you get a guarantee that the insertion completes with constant complexity, provided that new item to insert can be inserted immediately before the item pointed to by the iterator (as it will in both these cases).
Yes. The question absolutely has a point. When someone gave these functions their names they were thinking only of sorted arrays with repeating elements. If you have an array with unique elements, "std::lower_bound()" acts more like a search for an "upper bound" unless it finds the actual element.
So this is what I remember about these functions:
Failing to read the manual after a month or two since you last used these functions, almost certainly leads to a bug.
Both functions are very similar, in that they will find an insertion point in a sorted sequence that will preserve the sort. If there are no existing elements in the sequence that are equal to the search item, they will return the same iterator.
If you're trying to find something in the sequence, use
lower_bound
- it will point directly to the element if it's found.If you're inserting into the sequence, use
upper_bound
- it preserves the original ordering of duplicates.Returns an iterator pointing to the first element in the range [first, last) that is not less than (i.e. greater or equal to) value.
Returns an iterator pointing to the first element in the range [first, last) that is greater than value.
So by mixing both lower and upper bound you are able to exactly describe where your range begins and where it ends.
Yes.
Example:
imagine vector
prints: 4 4 4
http://en.cppreference.com/w/cpp/algorithm/lower_bound
http://en.cppreference.com/w/cpp/algorithm/upper_bound
There are already good answers as to what
std::lower_bound
andstd::upper_bound
is.I would like to answer your question 'how to remember them'?
Its easy to understand/remember if we draw an analogy with STL
begin()
andend()
methods of any container.begin()
returns the starting iterator to the container whileend()
returns the iterator which is just outside the container and we all know how useful they are while iterating.Now, on a sorted container and given value,
lower_bound
andupper_bound
will return range of iterators for that value easy to iterate on (just like begin and end)Practical usage::
Apart form the above mentioned usage on sorted list to access the range for a given value, one of the better applications of upper_bound is to access data having many-to-one relationship in a map.
For example, consider the following relationship: 1 -> a, 2 -> a, 3 -> a, 4 -> b, 5 -> c, 6 -> c, 7 -> c , 8 -> c, 9 -> c, 10 -> c
The above 10 mappings can be saved in map as follows:
The values can be access by the expression
(--map.upper_bound(val))->second
.For values of T ranging from lowest to 0, the expression will return
UND
. For values of T ranging from 1 to 3, it will return 'a' and so on..Now, imagine we have 100s of data mapping to one value and 100s of such mappings. This approach reduces the size of the map and thereby making it efficient.