Skip over navigation

Contents

Binary Search in Trees

Building a Binary Search Tree

Terms

Problems 1

In order to take advantage of the fast searching abilities of a binary search tree, it is first necessary to put your data into this format. For the following section, we will assume that we have the following functions to access the data. In your programs this may mean reading from a file or from standard input.


int data_remaining(void);
int next_data();

The first is a boolean that returns true while data remains and the second will supply the next piece of data. Realize that the data is coming in no particular order (ie. it is not presorted).

We want to build the tree with the following algorithm. We will read in a piece of data, find the appropriate place to add it into the tree (meaning we'll find a leaf that could have this piece of data as a child) and then add the data element in that spot.

First we will write a function that determines where the data element should be added to the tree. The return from the function will be the memory address where the data element should be stored. This means that if we find that the appropriate storage location is the right child of tree t, we would return &(t->right). The algorithm consists of walking left or right down the tree according to whether the data element is greater than or less than the data in the current node. We will also assume that the data is all unique, so there is no chance of the same number appearing more than once. It's possible to handle multiple instances of the same data element, but we'll ignore this situation for simplicity's sake.


tree_t  insertion_location(tree_t *t, int data)
{
	if (t == NULL) {
		return NULL;
	}
	tree_t  holder;

	/* Recursively find the insertion location. */
	if (data < t->data) {

		/* 
		 * Search for an insertion location further down the tree.
		 * If one isn't found, the insertion location should be
		 * the left pointer of t.
		 */
		holder = insertion_location (t->left);
		if(holder) {
			return holder;
		} else {
			return &(t->left);
		}
	} else {

		/* 
		 * Search for an insertion location further down the tree.
		 * If one isn't found, the insertion location should be
		 * the right pointer of t.
		 */
		holder = insertion_location (t->right);
		if(holder) {
			return holder;
		} else {
			return &(t->right);
		}
	}
}

If insertion_location returns null, whatever tree was passed as the argument should instead point to a new tree with that data element. Note that when walking through the tree, if suffices to check if the number is less than the data in the current node to determine whether it belongs in the left or the right subtree.

Now that we have written the more complex recursive part, we simply need to write the iterative function that calls the recursive one to build the tree.


tree_t *build_tree(void)
{
	int 	data;
	tree_t	*t, *new_tree, *insert_point;

	while (data_remaining()) {
		data = next_data();

		if((new_tree = new_tree(data)) == NULL) {
			return NULL;
		}

		insert_point = insertion_location(t, data);

		if (insert_point == NULL) {
			t = new_tree;
		} else {
			*insert_point = new_tree;
		}
	}

	return t;
}

Notice that whenever we call a function that returns dynamically allocated memory, it is always necessary to check for the possibility that allocation failed and returned a NULL pointer. The only time that insert_point will be NULL is when ~insertion_location~ is called for the first time, with a NULL pointer, that is, before there is anything in the tree.

Follow Us