C/C++ Binary Search Tree Data Structure

The binary search tree is very important. New data can be sorted instantly. Trees are used to sort tokens in programming languages and to index data in databases.

Searches and adding items are very fast, O(log(n)) (best case) O(n) (worse case) using Big O notation.

This is a link-based binary search tree, for an array based one see this page.

This binary search tree is written mostly in C. It uses the benefits of a C++ class constructor and destructor to make the code more readable. Note that the data that this tree sorts are passed as command line arguments so the executable's command line would look like: executable.exe John Mary Paul Aaron David Xavier Susan. The program output would look like this in the console window:

Aaron
David
John
Mary
Paul
Susan
Xavier
Press any key to continue . . .
#include <iostream>
using namespace std;

class Node
{
private:
	char *szName;
public:
	Node *left, *right;
	Node(char *szNodeName);
	~Node();
	char *GetNodeName();
};

Node::Node(char *szNodeName)
{
	left = right = 0;
	szName = szNodeName;
}

Node::~Node()
{
	if(left)
	delete left;
	if(right)
	delete right;
}

char *Node::GetNodeName()
{
	return szName;
}

void AddNode(Node **next, char *szNodeName)
{
	if((*next) == 0)
	*next = new Node(szNodeName);
	else
	{
	if(strcmp(szNodeName, (*next)->GetNodeName()) < 0)
		AddNode(&(*next)->left, szNodeName);
	else
		AddNode(&(*next)->right, szNodeName);
	}
}

void PrintNodes(Node *node)
{
	if(node)
	{
	PrintNodes(node->left);
	cout << node->GetNodeName() << endl;
	PrintNodes(node->right);
	}
}

int main(int argc, char *argv[])
{
	Node *root = 0;
	if(argc > 1)
	for(int i = 1; i < argc; AddNode(&root, argv[i++]));
	PrintNodes(root);
	if(root)
	delete root;
	return 0;
}

Now a binary search tree written in C++ using templates.

#include <iostream>
#include <string>
#include <stdlib.h>
#include <ctime>
using namespace std;

template <class TData> class Node
{
private:
	Node() {}
public:
	TData data;
	Node<TData> *pleft, *pright;
	Node(const TData &dataToSort) : pleft(nullptr), pright(nullptr), data(dataToSort) {}
	~Node()
	{
		if (pleft)
		{
			delete pleft;
			pleft = nullptr;
		}
		if (pright)
		{
			delete pright;
			pright = nullptr;
		}
	}
	bool IsLeaf() { return (pleft == nullptr && pright == nullptr); }
};

template <typename TData> void Forward(void ClientFunc(const TData &), const Node<TData> *tree)
{
	if (tree)
	{
		Forward(ClientFunc, tree->pleft);
		ClientFunc(tree->data);
		Forward(ClientFunc, tree->pright);
	}
}

template <typename TData> void Backward(void ClientFunc(const TData &), const Node<TData> *tree)
{
	if (tree)
	{
		Backward(ClientFunc, tree->pright);
		ClientFunc(tree->data);
		Backward(ClientFunc, tree->pleft);
	}
}

template <typename TData> void FillArray(Node<TData> *tree, Node<TData> **parray, unsigned int &index)
{
	if (tree)
	{
		FillArray(tree->pleft, parray, index);
		parray[index++] = tree;
		FillArray(tree->pright, parray, index);
	}
}

template <class TData> class BinarySearchTreeDataMembers
{
public:
	Node<TData> *ptree;
	unsigned int height, count;
	BinarySearchTreeDataMembers() : ptree(nullptr), height(0), count(0) {}
};

template <class TData> class BinarySearchTree
{
private:
	BinarySearchTreeDataMembers<TData> *dm;
	void CopyTree(const Node<TData> *tree); // this is used by the assignment operator
	void AddNode(Node<TData> *pnode); // this is used by the Balance() operation
	void AddArray(Node<TData> **parray, const unsigned int length);

public:
	BinarySearchTree();
	BinarySearchTree(const BinarySearchTree<TData> &obj); // copy constructor
	~BinarySearchTree();
	const BinarySearchTree<TData> &operator=(const BinarySearchTree<TData> &obj); // assignment operator
	void Add(const TData &data);
	bool Search(const TData &data) const;
	void Forward(void ClientFunc(const TData &)) const;
	void Backward(void ClientFunc(const TData &)) const;
	const TData *MaxValue() const; // return nullptr when tree is empty; this function could be useful to overload <, >, and == operators
	const TData *MinValue() const;
	unsigned int Height() const; // tree height; returns 0 if empty
	unsigned int Count() const; // node count; returns 0 if empty
	void Balance();
	void Clear();
};

template <class TData> BinarySearchTree<TData>::BinarySearchTree(const BinarySearchTree<TData> &obj) : dm(new BinarySearchTreeDataMembers<TData>())
{
	this->operator=(obj);
}

template <class TData> BinarySearchTree<TData>::BinarySearchTree() : dm(new BinarySearchTreeDataMembers<TData>())
{
}

template <class TData> BinarySearchTree<TData>::~BinarySearchTree()
{
	Clear();
	delete dm;
}

template <class TData> const BinarySearchTree<TData> &BinarySearchTree<TData>::operator=(const BinarySearchTree<TData> &obj)
{
	if (this != &obj)
	{
		Clear();
		CopyTree(obj.dm->ptree);
	}
	return (*this);
}

template <class TData> void BinarySearchTree<TData>::CopyTree(const Node<TData> *tree)
{
	if (tree)
	{
		Add(tree->data);
		CopyTree(tree->pleft);
		CopyTree(tree->pright);
	}
}

template <class TData> void BinarySearchTree<TData>::Add(const TData &data)
{
	Node<TData> **root = &dm->ptree;
	unsigned int height = 0;
	while (*root)
	{
		height++;
		if ((*root)->data == data)
		{
			if (height > dm->height)
				dm->height = height;
			return;
		}
		if (data < (*root)->data)
			root = &(*root)->pleft;
		else
			root = &(*root)->pright;
	}
	(*root) = new Node<TData>(data);
	dm->count++;
	height++;
	if (height > dm->height)
		dm->height = height;
}

template <class TData> unsigned int BinarySearchTree<TData>::Height() const
{
	return dm->height;
}

template <class TData> unsigned int BinarySearchTree<TData>::Count() const
{
	return dm->count;
}

template <class TData> void BinarySearchTree<TData>::AddNode(Node<TData> *pnode)
{
	Node<TData> **root = &dm->ptree;
	unsigned int height = 0;
	while (*root)
	{
		height++;
		if (pnode->data < (*root)->data)
			root = &(*root)->pleft;
		else
			root = &(*root)->pright;
	}
	(*root) = pnode;
	height++;
	if (height > dm->height)
		dm->height = height;
}

template <class TData> void BinarySearchTree<TData>::AddArray(Node<TData> **parray, const unsigned int length)
{
	if (length)
	{
		const unsigned int middle = length / 2;
		AddNode(parray[middle]);
		if (middle + 1 < length)
			AddArray(parray + middle + 1, length - middle - 1);
		if (middle)
			AddArray(parray, length - middle - (length & 1));
	}
}

template <class TData> void BinarySearchTree<TData>::Balance()
{
	dm->height = 0;
	const unsigned int count = dm->count;
	Node<TData> **parray = new Node<TData>*[count];
	unsigned int index = 0;
	FillArray(dm->ptree, parray, index);
	for (index = 0; index < count; index++)
		parray[index]->pleft = parray[index]->pright = nullptr;
	dm->ptree = nullptr;
	AddArray(parray, count);
	delete[]parray;
}

template <class TData> bool BinarySearchTree<TData>::Search(const TData &data) const
{
	Node<TData> *root = dm->ptree;
	while (root)
	{
		if (root->data == data)
			return true;
		if (data < root->data)
			root = root->pleft;
		else
			root = root->pright;
	}
	return false;
}

template <class TData> void BinarySearchTree<TData>::Forward(void ClientFunc(const TData &)) const
{
	::Forward(ClientFunc, dm->ptree);
}

template <class TData> void BinarySearchTree<TData>::Backward(void ClientFunc(const TData &)) const
{
	::Backward(ClientFunc, dm->ptree);
}

template <class TData> const TData *BinarySearchTree<TData>::MinValue() const
{
	Node<TData> *pnode = dm->ptree;
	while (pnode && pnode->pleft)
		pnode = pnode->pleft;
	if (pnode)
		return &pnode->data;
	return nullptr;
}

template <class TData> const TData *BinarySearchTree<TData>::MaxValue() const
{
	Node<TData> *pnode = dm->ptree;
	while (pnode && pnode->pright)
		pnode = pnode->pright;
	if (pnode)
		return &pnode->data;
	return nullptr;
}

template <class TData> void BinarySearchTree<TData>::Clear()
{
	if (dm->ptree)
	{
		delete dm->ptree;
		dm->ptree = nullptr;
		dm->count = dm->height = 0;
	}
}

// ######## TEST DRIVER CODE (THE CLIENT) ########

template <typename TData> void DataHit(const TData &data)
{
	cout << data << endl;
}

int main()
{
	{
		BinarySearchTree<int> bt;
		int n;
		time_t t = time(0);
		srand(t);
		for (int i = 0; i < 25; i++)
		{
			n = rand();
			cout << n << endl;
			bt.Add(n);
		}
		BinarySearchTree<int> bt2 = bt;
		bt = bt2;
		cout << "ENTER A NUMBER TO SEARCH: ";
		cin >> n;
		bool found = bt.Search(n);
		if (found)
			cout << "FOUND: " << n << endl;
		else
			cout << "NOT FOUND: " << n << endl;
		bt.Forward(DataHit);
		cout << endl;
		bt.Backward(DataHit);
		const int *p = bt.MaxValue();
		if (p)
		{
			cout << "MAX: " << *p << endl;
			cout << "MIN: " << *bt.MinValue() << endl;
		}
		cout << "COUNT: " << bt.Count() << endl;
		cout << "HEIGHT: " << bt.Height() << endl;
		cout << "BALANCING..." << endl;
		bt.Balance();
		cout << "COUNT: " << bt.Count() << endl;
		cout << "HEIGHT: " << bt.Height() << endl;
	}
	cout << endl;
	{
		BinarySearchTree<string> bt;
		string s;
		for (int i = 0; i < 20; i++)
		{
			char c[4] = { 0,0,0,0 };
			c[0] = c[1] = c[2] = ((rand() % 26) + 97);
			s = c;
			cout << s << endl;
			bt.Add(s);
		}
		cout << "ENTER A STRING TO SEARCH: ";
		cin >> s;
		bool found = bt.Search(s);
		if (found)
			cout << "FOUND: " << s << endl;
		else
			cout << "NOT FOUND: " << s << endl;
		bt.Forward(DataHit);
		cout << endl;
		bt.Backward(DataHit);
		const string *p = bt.MaxValue();
		if (p)
		{
			cout << "MAX: " << *p << endl;
			cout << "MIN: " << *bt.MinValue() << endl;
		}
	}
	return 0;
}