Introduction

In this article, I will show you how to use the Binary Search Tree to store data. And I am using templates for keys and data values to simplify usage of any data types. If you have good experience about the theory behind the binary search trees, you can skip next sections of the background.

Background

[1] Binary search trees are data structures which support many dynamic-set operations including search, insert, delete, minimum, maximum, predecessor, and successor. Basic operations on binary search tree take time proportional to the height of the tree. For a complete binary tree with n nodes, such operations take O(log n) time in the worst-case, as the height of randomly built binary search tree is proportional to log n. In some cases operations take O(n) time in case of sorted input, so the tree will be like a sorted list, so I used the Red-Black tree balancing technique, as described in the Tree Load Balancing section

A binary search tree is organized in a binary tree as shown in figure 1. Such a tree can be represented by a linked data structure in which each node is an object. In addition to the key field, each node contains fields left, right, and parent that point to the nodes corresponding to its left child, its right child, and its parent, respectively. If a child or the parent is missing, the appropriate field field contains a value NIL.

I will not dig in all Binary Tree functions here, I will just refer to the search function, and you can check reference [1] for more details, and I will talk about some of them in the code description, as many of them is so sophisticated like the Delete function.

Searching

The most common operation performed in binary search tree is searching for a key stored in the tree, which can be done in two ways:

The two ways begin search at the root and trace a path downward in the tree till they find the key or return NIL (not found case). First way code is more simple, but second way optimizes stack usage, which I am preferring in all my cases to deal with huge data without affecting the stack and the performance.

Code Description

All Tree functions encapsulated in a template class CBinaryTree and CBinaryTreeNode.

GetCount

returns tree nodes count with repetition (repeated key will assigned to one node).

In all my code here, I am avoiding recursion functions to avoid stack overflow, as I am dealing with huge data, so you will find all my code using <CODE lang=c++>while loops, sometimes code becomes complicated like the RemoveAll function:

RemoveAll function

This function removes all tree nodes, it does that by order to each node to delete its left child then its right child, then delete itself. This can be done with two ways:

Delete function

Delete function has three cases, I found it too complicated, and I hope I can describe it, The three cases are:

The node has no child, so we just remove it. As in you can see in figure 2, node K can be deleted by just resetting the pointer from node J.

The node has one child, so we need to splice it as node H in figure 2, we should make node D point to node F as a right child, and set node F parent to point to node D

The node has two children, so we choose its successor to take its position, and splice its successor (join successor parent and child, like the previous case) . For example, if we delete node C at figure 2, we get node C successor (D), so we will splice node D so that node I and H will be connected directly and node C will take node C place as in Figure 3.

Code in the Delete is optimized to handle the three cases altogether, so I find it hard to describe it here, but you can try to apply the code in each case and read comments carefully, and you will find it working well.

Class Usage

CBinaryTree usage is very simple, we just need to decide the KEY and DATA data types, then you can define the class, but you must choose a KEY that supports the compare function as it is used in the Insert and Search functions. For example:

Tree Load Balancing

Tree balancing can be achieved using many techniques. In my class here, I am using Red-Black Tree functions for the insertion in the tree. Red-Black Tree simply depends on keep tree height short as possible, as the search and the insertion operations time depend on the tree height. As in the following figure, if the sequence (C, A, B) is added to the tree, the height will be like case 1, but if we have an operation to change to the valid tree in case 3, it will be good as the tree height reduced.

So, I have included the functions LeftRotate, RightRotate, and RBInsert to can balance the tree functions.

Sample Demo

The attached source zip file with article contains a sample that parse any folder in a recursive function and parse all its files with the extension specified in the extensions editor (any text files format), and it adds all files tokens to a binary tree. Then you can navigate all tree tokens through the tree control. You can test it in VC6 or 7.1, or just run the attached exe, and don't hesitate to mail me for any help.

Source code files

BinaryTree.h: Binary Tree code.

RBTree.h: Red-Black Tree code.

References

Thanks to...

I awe a lot to my colleagues for helping me in implementing and testing this code. (JAK)

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.