
Why std::binary_search of std::list Works,
But You Shouldn't Use It!
David Kieras, University of Michigan
Prepared for EECS 381, 1/26/2013
A common error made by beginning users of the Standard Library containers and algorithms is to write code whose run-
time complexity (big-O) is a flaming disaster. These facilities were invented by algorithm and data structure fanatics who
took big-O really seriously; their goal was to make it possible for you to use "best of breed" data structures and algorithms
very easily. They certainly did not intend to help you write awful code easily!
In fact, in most cases, the Standard Library tries to keep you out of trouble by making it inconvenient to use in a really
inefficient way. But thanks to the great generality and power of the C++ templates used in the Standard Library, there are
some loopholes that allow you to write hopelessly inefficient code as easily as very efficient code. One such loophole is that
you can easily write Standard Library code that does a binary search on a linked list, which is so ridiculously inefficient that
saying "binary search on a linked list" is actually a geeky programming joke! Linked-lists are supposed to be searched
linearly! The purpose of this document is to explain why the Standard Library makes telling this joke so easy to do, and
demonstrate with some run-time comparisons why it is so bad.
The Standard Library allows you to apply the binary_search and lower_bound algorithms to any sorted sequence
container, including std::list, and it will produce a correct result. The following works for any sequence container:
!!binary_search(container.begin(), container.end(), probe)
However, if you look at any normal code for binary search (e.g. as in Kernighan and Ritchie), it is written to use array
subscripting. Array subscripting, a so-called random-access mechanism, runs in constant time, regardless of the size of the
array or value of the subscript. In contrast, a linked list has the property that the only way you can find a particular node is to
start at one end of the list and follow the links from one node to the next, checking them one at a time. Unlike with array
subscripting, there is no way to compute the location of a list node directly from its numerical position in the list - it could be
anywhere in memory. So how is it that you can do a binary_search on a std::list?
How binary_search is implemented. Below is a somewhat simplified copy of the Metrowerks Standard Library version
of lower_bound; the binary_search algorithm just calls lower_bound and checks the result (other implementations
might differ, but only in details).
template <class ForwardIterator, class T>
ForwardIterator
lower_bound(ForwardIterator first, ForwardIterator last, const T& value)
{
!typedef typename iterator_traits<ForwardIterator>::difference_type difference_type;
!difference_type len = distance(first, last);
!while (len > 0)
!{
!!ForwardIterator i = first;
!!difference_type len2 = len / 2;
!!advance(i, len2);
!!if (*i < value)
!!{
!!!first = ++i;
!!!len -= len2 + 1;
!!}
!!else
!!!len = len2;
!}
!return first;
}
1