Avoid deeply nested if
-statements
Deeply nested if
-statements are hard to read. You can reduce the amount of nesting here by inverting the first if
-statement and returning early, and avoiding the else
-statement by using the fact that the if
part already returns:
int binarySearch(int arr[], int low, int high, int n)
{
if (low > high)
{
return -1;
}
int i = (high + low) / 2;
if (arr[i] == n)
{
return i;
}
if (arr[i] > n)
{
return binarySearch(arr, low, i - 1, n);
}
else
{
return binarySearch(arr, i + 1, high, n);
}
}
Use size_t
for array indices
When dealing with sizes, counts and indices, prefer using size_t
to hold their values, as that type is guaranteed to be big enough to cover any array that can be addressed by your computer.
size_t
is unsigned, so it might look like you can't return -1
, but it turns out that integer promotion rules make it work regardless: you can return -1
, and the caller can check if (binarySearch(...) == -1)
, although perhaps better would be to create a constant for this, similar to std::string::npos
.
Try to make it more generic
Your binary search algorithm works for plain arrays of integers, but it doesn't work for anything else. What if you want to search in an array of float
s? What if you want to search in a std::vector
? The standard library provides std::binary_search()
which can work on a variety of containers holding any type that can be compared. It might be good practice to try to make the interface to your binary search implementation similar to that of std::binary_search()
. It is not as hard as it looks! You can start by making it a template for arrays of different types:
template<typename T>
size_t binarySearch(T arr[], size_t low, size_t high, const T &n)
{
...
}
Once you have done that excercise, try to have it take two iterators, and return an iterator to the element if it's found, or last
if not (assuming last
now points to right after the end of the range to search):
template<typename It, typename T>
It binarySearch(It first, It last, const T &n)
{
...
}
This is a bit more advanced; you have to work within the limitations of the iterators.
Last you might want to add another template parameter, typename Comp
, so you can provide a custom comparison operator.
const
for two reasons: 1 readability, 2 compiler can make more assumptions if caller and this function are in separate code units. \$\endgroup\$ – Dima Tisnek Nov 24 '20 at 9:23