Binary Search Tree

A binary search tree is a binary tree that may be empty, and every node must contain an identifier. An identifier of any node in the left subtree is less than the identifier of the root. An identifier of any node in the right subtree is greater than the identifier of the root. Both the left subtree and right subtree are binary search trees.

A binary search tree is shown in following Figure.

The binary search tree is basically a binary tree, and therefore it can be traversed in inorder, preorder, and postorder. If we traverse a binary search tree in inorder and print the identifiers contained in the nodes of the tree, we get a sorted list of identifiers in ascending order.
 
A binary search tree is an important search structure. For example, consider the problem of searching a list. If a list is ordered, searching becomes faster if we use a contiguous list and perform a binary search. But if we need to make changes in the list, such as inserting new entries and deleting old entries, using a contiguous list would be much slower, because insertion and deletion in a contiguous list requires moving many of the entries every time. So we may think of using a linked list because it permits insertions and deletions to be carried out by adjusting only a few pointers. But in an n-linked list, there is no way to move through the list other than one node at a time, permitting only sequential access. Binary trees provide an excellent solution to this problem. By making the entries of an ordered list into the nodes of a binary search tree, we find that we can search for a key in O(n logn) steps.
 
Program: Creating a Binary Search Tree
We assume that every node of a binary search tree is capable of holding an integer data item and that the links can be made to point to the root of the left subtree and the right subtree, respectively. Therefore, the structure of the node can be defined using the following declaration:

struct tnode
{
       int data;
       struct tnode *lchild,*rchild;
};

A complete C program to create a binary search tree follows:

#include <stdio.h>
#include <stdlib.h>
struct tnode
{
   int data;
   struct tnode *lchild, *rchild;
};

struct tnode *insert(struct tnode *p,int val)
{
   struct tnode *temp1,*temp2;
   if(p == NULL)
   {
      p = (struct tnode *) malloc(sizeof(struct tnode)); 
      /* insert the new node as root node*/
         if(p == NULL)
           {
      printf("Cannot allocate\n");
      exit(0);
          }
       p->data = val;
       p->lchild=p->rchild=NULL;
   }
  else
  {
   temp1 = p;
   /* traverse the tree to get a pointer to that 
   node whose child will be the newly created node*/
  while(temp1 != NULL)
  {
     temp2 = temp1;
     if( temp1 ->data > val)
          temp1 = temp1->lchild;
     else
          temp1 = temp1->rchild;
  }
  if( temp2->data > val)
  {
  temp2->lchild = (struct tnode*)malloc(sizeof(struct tnode));
  /*inserts the newly created node as left child*/
          temp2 = temp2->lchild;
          if(temp2 == NULL)
               {
          printf("Cannot allocate\n");
          exit(0);
              }
          temp2->data = val;
          temp2->lchild=temp2->rchild = NULL;
  }
  else
  {
     temp2->rchild = (struct tnode*)malloc(sizeof(struct tnode)); 
     /*inserts the newly created node as left child*/
      temp2 = temp2->rchild;
      if(temp2 == NULL)
           {
      printf("Cannot allocate\n");
      exit(0);
          }
     temp2->data = val;
     temp2->lchild=temp2->rchild = NULL;
}
}
return(p);
}
/* a function to binary tree in inorder */
void inorder(struct tnode *p)
   {
      if(p != NULL)
        {
           inorder(p->lchild);
          printf("%d\t",p->data);
         inorder(p->rchild);
         }
   }
void main()
{
   struct tnode *root = NULL;
   int n,x;
   printf("Enter the number of nodes\n");
   scanf("%d",&n);
   while( n -- > 0)
     {
         printf("Enter the data value\n");
         scanf("%d",&x);
         root = insert(root,x);
    }
     inorder(root);
 }

Explanation

  1. To create a binary search tree, we use a function called insert, which creates a new node with the data value supplied as a parameter to it, and inserts it into an already existing tree whose root pointer is also passed as a parameter.
  2. The function accomplishes this by checking whether the tree whose root pointer is passed as a parameter is empty. If it is empty, then the newly created node is inserted as a root node. If it is not empty, then it copies the root pointer into a variable temp1. It then stores the value of temp1 in another variable, temp2, and compares the data value of the node pointed to by temp1 with the data value supplied as a parameter. If the data value supplied as a parameter is smaller than the data value of the node pointed to by temp1, it copies the left link of the node pointed to by temp1 into temp1 (goes to the left); otherwise it copies the right link of the node pointed to by temp1 into temp1 (goes to the right).
  3. It repeats this process until temp1 reaches 0. When temp1 becomes 0, the new node is inserted as a left child of the node pointed to by temp2, if the data value of the node pointed to by temp2 is greater than the data value supplied as a parameter. Otherwise, the new node is inserted as a right child of the node pointed to by temp2. Therefore the insert procedure is:

    Input: 1. The number of nodes that the tree to be created should have
    2. The data values of each node in the tree to be created
     
    Output: The data value of the nodes of the tree in inorder
     
    Example
    Input: 1. The number of nodes that the created tree should have = 5
    2. The data values of the nodes in the tree to be created are: 10, 20, 5, 9, 8
     

Output : 5 8 9 10 20
 
Program
A function for inorder traversal of a binary tree:

void inorder(struct tnode *p)
    {
       if(p != NULL)
         {
 inorder(p->lchild);
 printf("%d\t",p->data);
    inorder(p->rchild);
}

A non-recursive/iterative function for traversing a binary tree in inorder is given here for the purpose of doing the analysis.

void inorder(struct tnode *p)
{
 struct tnode *stack[100];
 int top;
 top = −1;
if(p != NULL)
 {
    top++;
    stack[top] = p;
    p = p->lchild;
    while(top >= 0)
       {
          while ( p!= NULL)/* push the left child onto stack*/
           {
      top++;
                stack[top] =p;
     p = p->lchild;
   }
        p = stack[top];
   top-;
printf("%d\t",p->data);
   p = p->rchild;
   if ( p != NULL) /* push right child*/
      {
         top++;
                         stack[top] = p;
          p = p->lchild;
      }
   }
 }
}

A function for preorder traversal of a binary tree:

void preorder(struct tnode *p)
{
       if(p != NULL)
         {
             printf("%d\t",p->data);
             preorder(p->lchild);
             preorder(p->rchild);
          }

A function for postorder traversal of a binary tree:

void postorder(struct node *p) 
{
if(p != NULL)
{
	postorder(p->lchild);
	postorder(p->rchild);
	printf("%d\t",p->data);
}

Explanation

Consider the iterative version of the inorder just given. If the binary tree to be traversed has n nodes, the number of NULL links are n+1. Since every node is placed on the stack once, the statements stack[top]:=p and p:=stack[top] are executed n times. The test for NULL links will be done exactly n+1 times. So every step will be executed no more than some small constant times n. So the order of the algorithm is O(n). A similar analysis can be done to obtain the estimate of the computation time for preorder and postorder.
 
Constructing a Binary Tree Using the Preorder and Inorder Traversals
To obtain the binary tree, we reverse the preorder traversal and take the first node that is a root node. We then search for this node in the inorder traversal. In the inorder traversal, all the nodes to the left of this node will be the part of the left subtree, and all the nodes to the right of this node will be the part of the right subtree. We then consider the next node in the reversed preorder. If it is a part of the left subtree, then we make it the left child of the root; if it is part of the right subtree, we make it part of right subtree. This procedure is repeated recursively to get the tree as shown in following Figure :

For example, for the preorder and inorder traversals of a binary tree, the binary tree and its postorder traversal are as follows:
 
Z,A,Q,P,Y,X,C,B = Preorder
 
Q,A,Z,Y,P,C,X,B = Inorder
 
The postorder for this tree is:
 
Z,A,P,X,B,C,Y,Q