I am new to STL and used find() and upper_bound() functions on vector to find the position of 6 . The code is given below
#include <bits/stdc++.h>
using namespace std;
int main()
{
vector<int> sam ={1,2,5,3,7,8,4,6};
int f=upper_bound(sam.begin(), sam.end(), 6)- sam.begin();
vector<int>::iterator it;
it =find (sam.begin(), sam.end(), 6);
int d=it - sam.begin() ;
cout<<d<<" "<<f<<endl;
return 0;
}
The output when you run the code is 7 4 ,while I expected it to be 7 7 .
What am I doing wrong ?
cppreference.com for std::upper_bound() explains it nicely (emphasis mine):
Returns an iterator pointing to the first element in the range [first, last) that is greater than value, or last if no such element is found.
The range [first, last) must be partitioned with respect to the
expression !(value < element) or !comp(value, element), i.e., all
elements for which the expression is true must precede all elements
for which the expression is false. A fully-sorted range meets this
criterion.
In your case, you have a 7 (greater than 6, at index 4) appearing before a 4 (which is equal or less than 6), so the precondition is not met.
The idea of std::upper_bound() and its companions is to quickly do binary searches in sorted arrays. As opposed to linear search as in std::find(), it only needs O(log(n)) time complexity instead of O(n).
Related
In the following code both 7 and 4 are returning the same value i.e. 2 meanwhile every else number is returning the right index
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int main(){
vector<int> v;
v.push_back(1);
v.push_back(3);
v.push_back(7);
v.push_back(4);
v.push_back(9);
cout<<"lower bound : "<<lower_bound(v.begin(),v.end(),7)-v.begin()<<endl;
cout<<"lower bound : "<<lower_bound(v.begin(),v.end(),4)-v.begin()<<endl;
}
output:
lower bound : 2
lower bound : 2
I don't understand what's wrong with the code.
The vector should be sorted in advance. E.g.
std::sort(v.begin(), v.end());
cout<<"lower bound : "<<lower_bound(v.begin(),v.end(),7)-v.begin()<<endl; // ->3
cout<<"lower bound : "<<lower_bound(v.begin(),v.end(),4)-v.begin()<<endl; // ->2
lower_bound finds the first element that's greater-or-equal than the third parameter.
So lower_bound(v.begin(),v.end(),4) corretly finds 7.
On the other hand, lower_bound(v.begin(),v.end(),4) causes undefined behavior since your vector violates the precondition:
The range [first, last) must be partitioned with respect to the expression element < value or comp(element, value), i.e., all elements for which the expression is true must precede all elements for which the expression is false. A fully-sorted range meets this criterion.
vector<int> vec = {2,4,3};
vector<int>::iterator it;
it=lower_bound(vec.begin(),vec.end(),3);
cout<<*it;
This returns an output of 4 not 3 but
vector<int> vec = {2,3,4};
vector<int>::iterator it;
it=lower_bound(vec.begin(),vec.end(),3);
cout<<*it;
But this returns the correct output of 3. Please help me understand why it is failing in the corner case.
According to cppreference and its documentation of std::lower_bound:
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, or last
if no such element is found.
So, std::lower_bound returns the first element that is greater or equal to the value (3 here).
For {2, 4, 3}, the first element greater or equal to 3 is 4, but for {2, 3, 4,} it is 3.
P.S. According to the cppreference again:
The range [first, last) must be partitioned with respect to the
expression element < value or comp(element, value), i.e., all elements
for which the expression is true must precede all elements for which
the expression is false. A fully-sorted range meets this criterion.
Both of your vectors are partitioned correctly with the condition (element < value)
In short, the vector {2,4,3} doesn't meet the requirements of lower_bound.
https://en.cppreference.com/w/cpp/algorithm/lower_bound says:
The range [first, last) must be partitioned with respect to the
expression element < value or comp(element, value), i.e., all elements
for which the expression is true must precede all elements for which
the expression is false. A fully-sorted range meets this criterion.
If your vector is not sorted then use std::find.
From the documentation of std::nth_element we have:
template< class RandomIt >
void nth_element( RandomIt first, RandomIt nth, RandomIt last );
Partially sorts the range [first, last) in ascending order so that all
elements in the range [first, nth) are less than those in the range
[nth, last).
The thing that bothers me is the less word. Shouldn't it be less or equal? If the range is for example:
#include <algorithm>
#include <vector>
#include <iostream>
int main()
{
std::vector<int> numbers = {3, 2, 2, 2, 1};
auto middlePosition = numbers.begin() + 2;
std::nth_element(numbers.begin(), middlePosition, numbers.end());
for (int x : numbers)
std::cout << x << std::endl;
return 0;
}
The algorithm cannot make both numbers before the middlePosition less than 2, because there is only one such number. The algorithm does its best and the output is as desired:
1
2
2
3
2
Can I rely on such nice behavior?
My implementation (gcc 4.7) uses the introselect algorithm. Unfortunately I couldn't find the requirements on input of the algorithm. Does introselect need all values to be different?
I think, cppreference is incorrect here. Take loot at the Standard(N3337 25.4.2):
template<class RandomAccessIterator>
void nth_element
(
RandomAccessIterator first,
RandomAccessIterator nth,
RandomAccessIterator last
);
... For any iterator i in the range [first,nth) and any iterator j in
the range [nth,last) it holds that: !(*j < *i) or comp(*j,
*i) == false.
So, elements in range [first,nth) will be less or equal than elements in range [nth,last).
Here is a better definition of std::nth_element:
Rearranges the elements in the range [first,last), in such a way that
the element at the nth position is the element that would be in that
position in a sorted sequence.
The other elements are left without any specific order, except that
none of the elements preceding nth are greater than it, and none of
the elements following it are less.
No, it does not sort equal or less! nth_element randomly picks one of the values, that could be at the nth position in the sorted array. Since this would be the nth position there is no way, it can put all equal on the left side, since then it would not be the nth element anymore. Test it! The values equal appear on both sides of the nth position.
The following snippet is returning me 0. I expected it to be 1. What's wrong going on here?
#include <iostream>
#include <iterator>
#include <ostream>
#include <algorithm>
#include <vector>
using namespace std;
int main(){
vector<int> v;
int arr[] = {10,20,30,40,50};
v.push_back(11);
v.push_back(22);
copy(arr,arr + sizeof(arr)/sizeof(arr[0]),back_inserter(v)); // back_inserter makes space starting from the end of vector v
for(auto i = v.begin(); i != v.end(); ++i){
cout << *i << endl;
}
cout << endl << "Binary Search - " << binary_search(v.begin(), v.end(), 10) <<endl; // returns bool
}
I am using gcc /usr/lib/gcc/i686-linux-gnu/4.6/lto-wrapper
I ran the program and saw this:
11
22
10
20
30
40
50
Binary Search - 0
Your array is not sorted, therefore, binary search fails. (it sees 11 in the first position, and concludes 10 does not exist here)
You either want to ensure the array is sorted before binary searching or use the regular std::find.
binary_search says:
Checks if the sorted range [first, last) contains an element equal to
value. The first version uses operator< to compare the elements, the
second version uses the given comparison function comp.
Your list is not sorted, it contains the elements 11 and 22 prior to 10.
Your array is not sorted, so binary_search got undefined behavior. Try std::find instead
bool found = std::find(v.begin(), v.end(), 10) != v.end()
ยง25.4.3.4 of the C++11 standard (3242 draft)
Requires: The elements e of [first,last) are partitioned with respect to the expressions e < value and !(value < e) or comp(e,
value) and !comp(value, e). Also, for all elements e of [first, last),
e < value implies !(value < e) or comp(e, value) implies !comp(value,
e).
"Unexpected behavior"? There's nothing unexpected here.
The whole idea of binary search algorithm is taking advantage of the fact that the input array is sorted. If the array is not sorted, there can't be any binary search on it.
When you use std::binary_search (as well as all other standard binary search-based algorithms), the input sequence must be sorted in accordance with the same comparison predicate as the one used by std::binary_search. Since you did not pass any custom predicate to std::binary_search, it will use the ordering defined by < operator. That means that your input Sequence of integers must be sorted in ascending order.
In your case the input sequence does not satisfy that requirement. std::binary_search cannot be used on it.
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.