Copyright, 1997, Susan Anderson-Freed

The binary search also called the partition search uses a variation of a standard algorithm technique called Divide and Conquer. The standard Divide and Conquer Technique breaks a large problem into small pieces that are solved individually and then "glued" back together.

The table that follows contains the pseudo code for a typical divide and conquer algorithm.

Generic divide and conquer algorithm
find_solution(a_problem) {
   if (size.of.a_problem <= minimum_size)
     return results;
   else {
      split_problem a_problem;
      find_solution (a_problem1);
      find_solution (a_problem2);
      ...
       
      find_solution (a_problemn);
      
      glue_solutions_together;
    }
}    


Notice that the typical divide and conquer algorithm uses recursion to solve the subproblems independently. When the smallest subproblem is solved the results are glued together.

Tail Recursion

The binary search uses a variation of this technique referred to as tail recursion. This variation differs from the standard divide and conquer technique because it "throws" out a portion of the problem when the problem is split.

The following table shows a generic tail recursion algorithm.

Generic tail recursion algorithm
find_solution(a_problem) {
  if (size.of.a_problem <= minimum_size)
     return solution;
  else {
     split_problem a_problem;
     select_subproblem a_problemi;
     find_solution((a_problemi);
  }
}  


Notice that this algorithm contains only one recursive call which appears as the last statement in the algorithm, hence the name tail recursion.

Unwinding Tail Recursive Functions

Any tail recursive algorithm can be unwind into a comparable iterative algorithm. Since the recursive call appears last, the programmer does not need to create or manipulate a stack. The iterative version of our generic tail recursive algorithm appears in the following table.

Iterative version of a tail recursive algorithm
find_solution(a_problem) {
   while (size.of.a_problem > minimum_size) {
        split_problem;
        select ith subproblem
        a_problem = a_problemi
    } 
    return solution;
}


The Binary Search Algorithm

The binary search has the following characteristics:
  1. Ordered Data. Your information must be sorted on a primary key prior to conducting a binary search.
  2. Array Representation. You must use an array representation for your data. The binary search does not work with linked lists.
  3. Creation of a binary search tree. The binary search is so named because it creates a binary search tree from your sorted array. Since we are conducting a balanced tree search the worse case time of a binary search is O(log2n).


Examples of a binary search

The following table shows two sample C array with and even (10) and odd (11) number of elements.

Sample C arrays with odd and even number of elements.
Even Number [0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
10 20 30 40 50 60 70 80 90 100
Odd Number [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10]
5 10 20 30 40 50 60 70 80 90 100


The table that follows shows the binary search trees for these arrays. The actual algorithm does not construct the full tree but follows only a single branch until the element is found or a leaf node is reached.

Example Binary search Trees
Even Odd
     [4]
    /   \
  [1]   [7]
  / \   /  \
[0] [2][5] [8]
      \  \   \
      [3][6] [9]
    [5]
    /   \
  [2]   [8]
  / \   /  \
 [1] [3][6] [9]
 /     \  \   \
[0]    [4][7] [10]      


The Binary search Program

The following table contains the constant definitions and the function prototypes used by the binary search program. Two of the functions, selection_sort() and swap(), are used to sort the array of random numbers prior to conducting the binary search. We won't examine these functions until we discuss sorting.

Definitions and Declarations for the Binary Search
#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 101
#define NOT_FOUND -100
#define LESS -1
#define SAME 0
#define GREATER 1

#define COMPARE(x, y) ((x) < (y) ? LESS : (x) == (y) ? SAME : GREATER)

void create_array(int [], int *);
void print_array (int [], int);
void selection_sort(int [], int);
int binary_search(int [], int, int);
void swap(int *, int *);


The main() Function

A sample main() function used for the binary search appears in the following table. As the code indicates, the user is allowed to search for numbers in the array until she or he enters a negative one.

The binary search returns the array position of the number if it is in the array; otherwise it returns NOT_FOUND.

The main() function for the binary search
int main()
{

   int size,searchnum,position;
   int numbers[MAX_SIZE];

   create_array(numbers, &size);
   selection_sort(numbers,size);
   print_array(numbers, size);
 
   printf("Enter the number to find: (-1 to quit): ");
   scanf("%d",&searchnum);
   while ( searchnum > -1)  {
      if ((position = binary_search(numbers,searchnum,size)) == NOT_FOUND)
         printf("The number is not in the array \n\n");
      else
         printf("The number was found in the %d position\n\n",position);
      printf("Enter the number to find (-1 to quit): ");
      scanf("%d",&searchnum);
   }
}


The binary search function

The following table contains the binary search function. The action of the algorithm is as follows:
  1. The boundaries of the current search begin with the the first and last array positions, denoted lower_bound and upper_bound, respectively.
  2. If the lower bound exceeds the upper bound, the key is not in the array and a NOT _FOUND is returned.
  3. If the lower bound is less than or equal to the upper bound, a while statement is entered. This statement:
    1. Determines the middle position of the array based on the current boundaries. This is found by adding the two bounds and dividing by two.
    2. If the key equals the value in the middle position the position is returned.
    3. If value in the middle position is less than the search key, the lower bound is moved to the middle position+1
    4. IF the value in the middle position is greater than the search key, the upper bound is moved to the middle position-1.


The Binary Search
int binary_search(int numbers[], int searchnum, int size)
{
  int lower_bound = 0, upper_bound = size-1, middle_position;
  while (lower_bound <= upper_bound)  {
    middle_position = (lower_bound+upper_bound)/2;
    printf("Lower bound %d  Middle Position %d", lower_bound, middle_position);
    printf(" Upper bound  %d\n",upper_bound);
    switch (COMPARE(numbers[middle_position], searchnum)) {
      case LESS : lower_bound = middle_position + 1;
		break;
      case SAME  : return middle_position;
      case GREATER  : upper_bound = middle_position - 1;
    }
  }
  return NOT_FOUND;
}