Java Arena
Step 6 of 6

Binary search

Think of a guessing game: someone picks a number from 1 to 100, and after each guess tells you "higher" or "lower". Guessing 1, 2, 3 and so on is a linear search, and could take 100 guesses. A smarter first guess is 50: whatever the answer, half of the numbers are ruled out. Then guess the middle of the half that's left, and so on. Every guess halves what's left, and 7 guesses are always enough.

Binary search plays this game with a sorted array. It looks at the middle element of the range where the value could still be:

  • If it's the wanted value, the search is done.
  • If it's smaller than the wanted value, the wanted value can only be to its right, since the array is sorted. The range becomes the right half.
  • If it's larger, the range becomes the left half.

It repeats this until it finds the value, or until the range is empty, which means the value isn't there.

In code

Two variables, low and high, are the first and the last index of the range. At first, the range is the whole array. Here a race keeps the bib numbers of the runners who finished, sorted:

public class Main {
    public static void main(String[] args) {
        int[] finishers = {3, 8, 12, 15, 21, 27, 30, 34, 41};
        System.out.println(findInSorted(finishers, 27));
        System.out.println(findInSorted(finishers, 13));
    }

    public static int findInSorted(int[] sorted, int wanted) {
        int low = 0;
        int high = sorted.length - 1;
        while (low <= high) {
            int middle = (low + high) / 2;
            if (sorted[middle] == wanted) {
                return middle;
            }
            if (sorted[middle] < wanted) {
                low = middle + 1;
            } else {
                high = middle - 1;
            }
        }
        return -1;
    }
}
Output
5
-1

The search for 27, round by round:

low high middle sorted[middle] Then
0 8 4 21 smaller than 27: low = 5
5 8 6 30 larger than 27: high = 5
5 5 5 27 found: return 5

And for 13, which isn't there:

low high middle sorted[middle] Then
0 8 4 21 larger than 13: high = 3
0 3 1 8 smaller than 13: low = 2
2 3 2 12 smaller than 13: low = 3
3 3 3 15 larger than 13: high = 2

Now low is 3 and high is 2: the range is empty, the loop ends, and the method returns -1.

  • (low + high) / 2 is integer division, so the middle is always a whole index. When the range has an even number of elements, it's the left one of the two in the middle.
  • The middle element has been checked, so the new range leaves it out: middle + 1 and middle - 1. With low = middle, the range could stop shrinking, and the loop would never end.
  • The loop runs while (low <= high): when low equals high, the range still has one element to look at.

It needs sorted data

Binary search throws away half of the range at every look, trusting that everything to the left of the middle is smaller and everything to the right is larger. In an array that isn't sorted, that isn't true, and the search can miss a value that's there. So sort the data first (with Arrays.sort or Collections.sort), or keep it sorted as values are added. Sorting takes time too, so binary search pays off best when the same data is searched many times.

Java has binary search ready as well, in Arrays.binarySearch and Collections.binarySearch, for sorted arrays and lists. In this module you write your own, to see how it works.

How much work is it?

Each look at a middle value halves the range, so the range runs out fast:

Values Values looked at, linear search Values looked at, binary search
100 100 7
1,000 1,000 10
1,000,000 1,000,000 20
1,000,000,000 1,000,000,000 30

A sorted array of a million values needs at most 20 looks, because 2 to the 20th power is 1,048,576, more than a million: halving the range 20 times always gets it down to nothing. Each look compares the middle value with the wanted one up to twice (==, then <), so that's at most 40 comparisons, where linear search may need a million. Twice as many values take only one look more.

Your task

Challenge 1 of 3

A mail carrier has a sorted array of the house numbers that get mail today. Fill in the blanks of findHouse, a binary search that returns the index of a house number, or -1 if the house gets no mail. The check runs:

int[] houses = {2, 4, 8, 10, 14, 16, 20, 22, 26};
System.out.println(findHouse(houses, 20));
System.out.println(findHouse(houses, 12));

and expects:

6
-1

Challenge 2

This binary search prints each value it looks at. What does the program print?

Challenge 3

How many values does binary search look at? Write the method looksNeeded(int[] sorted, int wanted). It does the binary search of this step, with the middle at (low + high) / 2, but instead of an index it returns how many middle values it looked at, whether it found the wanted value or not.

The check makes a sorted array of the 1,000 even numbers from 0 to 1998, and looks for 998, 1998 and 7. A linear search would look at 500, 1,000 and 1,000 values for them. (998 happens to sit right in the middle of the array.) The check runs:

int[] evens = new int[1000];
for (int i = 0; i < evens.length; i++) {
    evens[i] = i * 2;
}
System.out.println(looksNeeded(evens, 998));
System.out.println(looksNeeded(evens, 1998));
System.out.println(looksNeeded(evens, 7));

and expects:

1
10
10