Suppose is a sorted list of length containing distinct elements. Given a specific target element (which may or may not be in the list), how could we find that element (or fail)?

Binary Search

Choose some value at index in the array. Then, given that the list is sorted, we can make some key insights that drive the idea behind binary search:

  • If , we guarantee that if exists, it is in some index less than .
  • If , we guarantee that if exists, it is in some index greater than .

We can use this fact to restrict our search interval (by dropping all indices we know isn’t in), until we either find or don’t.

If we do this starting from the middle of the array, we can continuously cut the size of our array in half until we find or don’t. Consider the following example.

Example: Binary Search Algorithm

Suppose we’re looking for element 5. Then, starting from the middle of the array,

  1. Examining index 3 to find 7, we know that because , if 5 exists, it must be at an index less than 2. Thus, we will examine the subarray to the left of index 3 next.
  2. Examining the middle of the left subarray, index 1 to find 3, we know that because , if 5 exists, it must be at an index greater than index 1. Thus, we examine the subarray to the right of index 1 next.
  3. We the only element in this right subarray to find 5.

The algorithm is given below (in Python)

def binary_search(array, target):
    left = 0
    right = len(array) - 1
 
    while left <= right:
          center = (left + right) // 2 # integer division
 
          if array[center] == target: # element found!
             return center
          else: # element not found. restrict our interval
                if target < array[center]: 
                   right = center - 1
                elif target > array[center]:
                   left = center + 1
 
    return -1

Note that binary search is often known as a decrease and conquer method, as our list search interval is decreasing every iteration.

Let’s perform a time complexity analysis on Binary Search.

  • In the best case, the target is the first element we check in the array. In this case, our while loop only executes once, giving us .
  • In the worst case, the target does not exist in the array. In this case, the binary search continually divides the array in half, so for iteration , we have list size . The algorithm will stop when the list size is 1, so we have a total of iterations.
    But when the list size is 1, the while loop will still execute once, giving us total iterations, and time complexity .

We now analyze the average case for Binary Search. To do this, we find the expected value of choosing any random element (uniformly) from a list of length . To help simplify the calculations, we will focus on lists with length .

  1. In the case, our while loop iterates once.
  2. In the case, if the element is in the middle, our while loop iterates once. Otherwise, we perform case (1) 2 times, once for the left subarray and once for the right subarray. Thus, we have 1 element with 1 iteration, and 2 elements with 2 iterations.
  3. In the case, if the element is in the middle, our while loop iterates once. Otherwise, we perform case (2) 2 times, once for the left subarray and once for the right subarray. Recursively continuing this, we will have 1 element with 1 iteration, 2 elements with 2 iterations, and 4 elements with 3 iterations.

We can continue this argument to find that generally, for iterations, we have elements in the list. Given that every element has an equal chance of occurring, we find expected value

which comes out to be .