binary search on c++ with comparison - c++

I would like to perform a binary search on c++, on a sorted vector V.
In particular, I am not interested in finding a exact value of the entry of the vector. I would like to find the position j of the entry which satisfy V[j-1] <= X < V[j], where X is an input values.
for example:
for a vector v={1, 4, 7, 12, 17, 55} and X=8, the function should return 3.
Can I use the STD function binary_search, with O(log(2)) complexity?
And how?
Many thanks,
Al.

The standard functions for this are upper_bound and lower_bound. Read these
http://www.cplusplus.com/reference/algorithm/upper_bound/
http://www.cplusplus.com/reference/algorithm/lower_bound/
If you scroll down these pages a bit, you will find examples which should make things clear :)

A note on the functions Bartosz linked:
upper_bound(begin, end, value) returns the first element greater than the given value. This is the end (or one-past-the-end) of the interval where it could be inserted and preserve ordering
lower_bound(begin, end, value) return the first element not less than the given value. This is the beginning of the interval where it could be inserted
So in practice:
std::vector<int> v = {1, 4, 7, 12, 17, 55};
int x = 8;
auto first = std::lower_bound(v.begin(), v.end(), x);
auto last = std::upper_bound(first, v.end(), x);
should give first == last && *first == 12. This is because the half-open interval [first,last) where x could be inserted is empty.
Note that this is generally more useful than what you actually asked for, because
std::vector::insert(iterator, value)
inserts before the given iterator, so you can always use the result of upper_bound here.

Per your requirement, the correct STL function to use is upper_bound. Syntax:
upper_bound(V.begin(),V.end(),val)-V.begin()
Will return the 0 based index you seek.

Related

What is std::views::counted?

On https://en.cppreference.com/w/cpp/ranges, std::views::counted is listed in the range adaptors section. However, it is not tagged as range adaptor object.
I guess that why I can't write using the pipe operator like:
std::vector<size_t> vec = {1, 2, 3, 4, 5};
auto view = vec | std::ranges::counted(... ; // does not compile
My questions are:
what is a std::ranges::counted? Why is it listed in the range adaptor section?
what are the use cases? what are the advantages over using take and drop?
Cppreference is following the organization of the C++20 standard. And it puts views::counted into the "Range Adaptors" section. Despite the fact that the standard says:
These adaptors can be chained to create pipelines of range transformations that evaluate lazily as the resulting view is iterated.
This is not true of the behavior of views::counted. Indeed, most of the other elements in that section say that their customization points "denotes a range adaptor object" (which describes the piping functionality), but views::counted does not.
It's unclear why they put it in that section, but it is a useful type in-and-of itself. It's really just an efficient way of saying subrange(it, it + n). It is efficient in that it doesn't actually increment the iterator by n.
The advantage it has over take_view is that take_view operates on a range, while all counted needs is an iterator. The main difference is that counted assumes that there are n valid iterator positions (and will give UB if that is not the case), while take_view does not. take_view will give you up to n objects, but if the range is shorter than that (as defined by the sentinel), it doesn't try to iterate past the end of the range.
According to the docs
A counted view presents a view of the elements of the counted range [i, n) for some iterator i and non-negative integer n.
A counted range [i, n) is the n elements starting with the element pointed to by i and up to but not including the element, if any, pointed to by the result of n applications of ++i.
So essentially it returns a slice given a starting iterator and a number of elements to include after that iterator. The example shown in the docs is
#include <ranges>
#include <iostream>
int main()
{
const int a[] = {1, 2, 3, 4, 5, 6, 7};
for(int i : std::views::counted(a, 3))
std::cout << i << ' ';
std::cout << '\n';
const auto il = {1, 2, 3, 4, 5};
for (int i : std::views::counted(il.begin() + 1, 3))
std::cout << i << ' ';
std::cout << '\n';
}
Output
1 2 3
2 3 4
Comparing the specific functions you listed, here are their summaries:
std::ranges::views::take: a view consisting of (up to) the first N elements of another view
std::ranges::views::drop: a view consisting of elements of another view, skipping (up to) the first N elements
std::ranges::views::counted: creates a subrange from an iterator and a count, always containing exactly N elements
views::counted is similar to views::take. However, the former accepts an iterator instead of a range.
One of the benefits of views::counted over views::take is that it allows us to construct a sized input/output range when we already know its size:
auto ints = views::istream<int>(std::cin);
auto counted = views::counted(ints.begin(), 4);
auto take = views::take(ints, 4);
static_assert(ranges::sized_range<decltype(counted)>); // ok
static_assert(ranges::sized_range<decltype(take)>); // failed
Unlike views::take, since we omit the sentinel information, we must ensure that the iterator is still valid after incrementing n times, while the former is guaranteed to always return a valid range.

the n should be inclusive or exclusive when using std::nth_element?

Hi I have a question on the usage of std::nth_element.
If I want to obtain the k-th largest element from a vector, should it inclusive or exclusive?
int k = 3;
vector<int> nums{1,2,3,4,5,6,7};
std::nth_element(nums.begin(), nums.begin()+k-1, nums.end(), [](int& a, int& b){return a > b;});
int result = nums[k-1]
or
int k = 3
vector<int> nums{1,2,3,4,5,6,7};
std::nth_element(nums.begin(), nums.begin()+k, nums.end(), [](int& a, int& b){return a > b;});
int result = nums[k-1]
It reminds me that when we get a subarray using iterator, it should be exclusive?
for example,
vector<int> sub(nums.begin(), nums.begin()+k);
So, the n for nth_element is also exclusive?
This is the type of question that is best to figure out and understand by playing around with a bunch of examples on your own.
However, I'll try to explain why you're getting the same answer in both of your examples. As explained in cppreference, std::nth_element is a partial sorting algorithm. It only guarantees that, given an iterator to an element n as its second argument:
All of the elements before this new nth element are less than or equal to the elements after the new nth element.
("Less than or equal to" is the default behavior if you don't pass a special comparison function.)
That means if you use nums.begin()+k-1 in one case and nums.begin()+k in another case as the second argument to std::nth_element, then in the latter case the partial sorting algorithm will include one additional item in the sort. In that case, you are dividing the vector between larger and smaller items at a spot one index higher than in the first case. However, the (default) algorithm only guarantees that each of the items in the "small half" of the vector will be smaller than each of the items in the "large half," not that the two halves are sorted within themselves.
In other words, if you've done a partial sort through nums.begin()+k, there is no guarantee that nums[k-1] will be the next-smallest (or in your case, the next-largest) number in the entire vector.
With certain inputs, like your {1, 2, 3, 4, 5, 6, 7}, or {9, 4, 1, 8, 5}, you do happen to get the same answers.
However, with many others, like {1, 4, 9, 8, 5}, the results do not match:
int k = 3;
vector<int> nums{1,4,9,8,5};
auto numsCopy = nums;
// First with +k - 1
std::nth_element(nums.begin(), nums.begin()+k-1, nums.end(), [](int& a, int& b){return a > b;});
// Then with only +k
std::nth_element(numsCopy.begin(), numsCopy.begin()+k, numsCopy.end(), [](int& a, int& b){return a > b;});
cout << nums[k-1]; // 5
cout << numsCopy[k-1]; // 9
Demo
Can you figure out why that is?
Also, to clearly answer your question about inclusive vs exclusive, as #Daniel Junglas pointed out in the comments, the second argument to std::nth_element is meant to point directly to the item you wish to be changed. So if it helps you, you can think of that as "inclusive." This is different from the third argument to std::nth_element, the end iterator, which is always exclusive since .end() points beyond the last item in the vector.

Does the STL offer something to find the last element for which a predicate is true in a range defined by two non-reverse iterators? [duplicate]

I am working on an exercise where I have a vector and I am writing my own reverse algorithm by using a reverse and a normal (forward) iterator to reverse the content of the vector. However, I am not able to compare the iterators.
int vals[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
vector<int> numbers(vals, vals + 10);
vector<int>::iterator start = numbers.begin();
vector<int>::reverse_iterator end = numbers.rend();
I have a previous algorithm for reversing the vector by using two iterators, however in this task I am not able to compare them using the != operator between them. My guess would be to get the underlying pointers or indexes in the vector with each other but how do I get the pointers/index?
Do a comparison using the the iterator returned by base(): it == rit.base() - 1.
You can convert a reverse_iterator to iterator by calling base().
Be careful however, as there are some caveats. #Matthieu M.'s comment is particularly helpful:
Note: base() actually returns an iterator to the element following the
element that the reverse_iterator was pointing to.
Checkout http://en.cppreference.com/w/cpp/iterator/reverse_iterator/base
rit.base()
returns a 'normal' iterator.
You can use (&*start == &*(end - 1)) to directly compare the address that the iterator is pointing to.
The two types cannot be compared (which is a very good idea) and calling .base() is not very elegant (or generic) in my opinion.
You can convert the types and compare the result.
Taking into account the off-by-one rule involving reverse_iterators.
Conversion from iterator to reverse_iterator need to be explicit (fortunately), however, conversion from reverse_iterator to iterator is not possible (unfortunately).
So there is only one way to do conversion and then make the comparison.
std::vector<double> vv = {1.,2.,3.};
auto it = vv.begin();
auto rit = vv.rend();
// assert( it == rit ); // error: does not compile
assert(std::vector<double>::reverse_iterator{it} == rit);

Binary search to find less or equal value in STL C++ multiset

We have STL (Standard Template Library) multiset, we want to implement a binary search that will give us the first less-or-equal element compared to some value x
From this post: lower_bound == upper_bound, we see that we can use the standar lower_bound and upper_bound to find greater values compared to x, what about finding smaller or equal.
Is it possible to do such thing?
Just use the upper_bound member function of the multiset. upper_bound will return the first element that is grater than the value you are searching for. That means the iterator before that will be the first element that is equal to or smaller than the value. So in
int main()
{
std::multiset<int> ms = {1,1,2,2,3,3,4,4,5,5};
auto end = ms.upper_bound(3);
for (auto it = ms.begin(); it != end; ++it)
std::cout << *it;
}
It will print 112233 since that is all the element less than or equal to 3.
Of course you will need to make sure upper_bound does not return begin() which would mean there are no elements less than or equal to what you are searching for.

why C++ lower_bound() allows return pointer equivalent to val while upper_bound() does not

I read the description for C++ upper_bound() function and lower_bound() function. It is interesting for me that upper_bound() only returns the first iterator with value > val (or return last iterator in the range [first, last) if val not found).
The implementation is different from lower_bound(), while it returns the first iterator NOT SMALLER than val, so it allows the return pointer equivalent to val.
I am just curious to know what is the purpose to design upper_bound() in this way that upper_bound() must NOT return an iterator with value equivalent to val?
For example:
vector<int> a = {1, 2, 3, 4};
auto i = lower_bound(a.begin(), a.end(), 2); // i is iterator at 2;
auto j = upper_bound(a.begin(), a.end(), 2); // j is iterator at 3;
http://www.cplusplus.com/reference/algorithm/lower_bound/
In C++, iterators usually work in pairs. The first iterator points to the first element to consider, and the last iterator points to one-past-the-last element to consider. This is to make looping easy:
for(it cur=first; cur!=last; cur++)
As such, lower_bound and upper bound together, form a "range of all elements equal to the item you searched for. The same range that std::equal_range returns.
upper_bound is useful in cases like std::multimap where values are stored in sorted order. It lets you traverse within a range where starting position can be decided by lower_bound, and end-before-this-position is decided by upper_bound.
for (iter_type it = myMap.lower_bound("key_val"); it != myMap.upper_bound("key_val"); it++)
This for loop would traverse till the point key == key_val.