Skip to content
CppCooperProgramming Portfolio

Article

Three Ways to Write Binary Search

AlgorithmsData Structures

I wanted to implement binary search in as many ways as I could think of. The result was three Java versions: one driven by a while loop, one by a signed increment in a for loop, and one by recursion.

The point of the exercise was the algorithm. Changing the control flow gives you another way to think about how the search progresses, but it should not quietly change what the caller gets back.

Keep the questions the same

The implementations share an abstract interface for looking up a value, finding an insertion position and finding neighboring positions. Common behavior is factored into that shared structure, while the variants supply their different search mechanics.

Version A updates a search interval inside a while loop. Version B expresses its progression through an increment-driven for loop. Version C uses recursive calls to continue the search. Each still has to make progress toward a result and decide what to return when the target lies between stored values.

That last part makes the comparison more interesting than finding one number that happens to be present. A search interface also needs a consistent answer for targets before, after and between the elements used by the exercise.

Run the same behavior against each version

The original suite applies four shared tests to each implementation, giving twelve test cases. Those cases use increasing lists of five and six values and check lookup, insertion and adjacent positions. The three implementations pass all twelve with the JUnit Platform runner.

Additional finite checks compare the methods with independent linear scans on non-empty, sorted, unique integer lists. Across the three versions, 238,164 method checks pass, including small exhaustive cases, integer extremes and seeded random inputs. That gives the variants a broader common comparison without turning a test count into a proof.

Be precise about the input

The public list does not enforce sorting, uniqueness or a non-empty input. Empty-list probes throw an index exception. With duplicate values, neighboring positions can contain the target value itself, so a returned adjacent index need not mean a strictly lower or higher value.

Error handling was outside my goal for this exercise. The useful comparison is therefore the alternative implementations within the checked input domain. The shared tests keep that comparison concrete. A speed comparison would need a separate experiment, with fixed inputs and measurement conditions.