The main thing I would change is the input type. If you look at the standard library you will find that containers and algorithms are connected via iterators. Thus allowing algorithms to be written for any container type that supports the appropriate iterators.
In your case; why only std::vector
? This same algorithm should work for std::array
and now you mention it C-Arrays
. Which brings us to the return type. If you look at most finding
type algorithms they will return an iterator to the element they found; if nothing is found then they return an element to end()
.
There is no harm adding a wrapper that takes a container then calls the underlying code with std::begin()
and std::end()
.
So first I would change your function to be just a wrapper.
std::vector<int>::const_iterator BinarySearch(const std::vector<int>& array, int key)
{
return BinarySearch(std::begin(array), std::end(array), key);
}
While we are talking about iterators and generalizing the code.The wrapper can be generalized to any container type (because nothing in the function is dependent on it being a std::vector
) by simply adding a template.
template<tyepname C>
auto BinarySearch(C const& cont, int key)
{
return BinarySearch(std::begin(cont), std::end(cont), key);
}
Range is from beginning to one past end generally.
auto lower = array.begin();
auto upper = array.end()-1;
You have taken the stance that your ranges are inclusive of end. As you will notice this actually makes your code harder when you have empty ranges. But also it makes your split inaccurate.
auto mid = lower + (upper-lower) /2;
To be correct that should have been:
auto mid = lower + ((upper-lower) + 1) /2;
I believe you will find that you slightly pesimizing searches for big numbers but slightly optimizing searches for small numbers.
The other things is if you have multiple values that match your key then you return a random one of these values. It might be more logical to return the first one? If you want to return a random one then you should definitely document that fact.
template<typename I>
I BinarySearch(I begin, I end)
{
I lower = begin;
I upper = end;
while (lower < upper)
{
I mid = lower + std::distance(upper, lower) / 2;
if(key == *mid)
{
return mid;
}
if(key < *mid) {
upper = mid;
}
else {
lower = mid;
}
}
return end;
}