Binary Search Tree in C++

Implementation of binary search tree in C++.
Github: sourcecode/binarysearchtree.h

#include <iostream>
#include "queueusinglinkedlist.h"

using namespace std;

/* Binary search tree class */
template <typename T> class binarysearchtree
{
private:
	//Node class
	template <typename T>class Node
	{
	public:
		Node<T>* left; //left tree
		Node<T>* right; //right tree
		T data; //element
		//constructor
		Node() :left(nullptr), right(nullptr)
		{
		}
	};

	Node<T>* root; //root node of the BST

public:
	//constructor
	binarysearchtree():root(nullptr)
	{
	}

	//recursive method to add the element
	void addrecursive(T elem)
	{
		if (findelement(root,elem))
		{
			return;
		}
		//call this recursive function
		root = addrecursive(root, elem);
	}
	//recusrive call to add element
	Node<T>* addrecursive(Node<T>* node, T elem)
	{
		//if node is null
		if (node == nullptr)
		{
			//create new node and return it
			Node<T>* node = new Node<T>();
			node->data = elem;
			return node;
		}
		else
		{
			//element is less than the node element , then traverse the left tree
			if (elem < node->data)
			{
				node->left = addrecursive(node->left, elem);
			}
			else
			{
				//element is greater than the node element , then traverse the right tree
				node->right = addrecursive(node->right, elem);
			}
		}
		return node;
	}

	//add the elemenet in the iterative method
	void add(T elem)
	{
		//create new node
		Node<T>* new_node = new Node<T>();
		new_node->data = elem;

		//root node creation
		if (root == nullptr)
		{
			root = new_node;
		}
		else //traverse the tree and add element accordingly 
		{
			Node<T>* node = root;

			//travese the tree
			while (node != nullptr)
			{
				if (elem < node->data) //travese left tree, if element is less than the node data
				{
					if (node->left == nullptr)
					{
						node->left = new_node;
						return;
					}
					else
					{
						node = node->left;
					}
				}
				else if(elem > node->data) //traverse the right tree, if element is greater than the node data
				{
					if (node->right == nullptr)
					{
						node->right = new_node;
						return;
					}
					else
					{
						node = node->right;
					}
				}
			}
		}
	}
	//level order traversal
	void print_levelordertraversal()
	{
		if (root == nullptr)
			return;
		
		Node<T>* node = root;

		cout << "level order traversal ";
		//use the custom queue designed from linked list
		queuelist<Node<T>*>* queue = new queuelist<Node<T>*>();

		//add the root node to the queue
		queue->enqueue(node);

		//traverse until queue is empty
		while (!queue->isEmpty())
		{	
			 //get the node
			 node = queue->dequeue();
			 if (node == nullptr)
			 {
				 cout << "null   ";
			 }
			 else
			 {
				 //print the node value
				 cout << node->data << " ";
				 //if node's left node is valid
				 if (node->left)
				 {
					 //add the left node to the queue for traversal
					 queue->enqueue(node->left);
				 }
				 //if node's right node is valid
				 if (node->right)
				 {
					 //add the right node to the queue for traversal
					 queue->enqueue(node->right);
				 }
			 }
		}

		cout << endl;
	}

	//remove the element
	void remove(T elem)
	{
		if (root == nullptr)
		{
			cout << "no element in bst" << endl;
			return;
		}
		else
		{
			//element is found
			if (findelement(root, elem))
			{
				root = remove(root, elem);
			}
			else cout << "element not found in bst" << endl;
		}
	}

	//find the element
	bool findelement(Node<T>* node, T elem)
	{
		//travese till the node is not nullptr
		while (node)
		{
			//element is same as node->data
			if (node->data == elem)
			{
				return true;
			}
			else if (node->data > elem)
			{
				//element is less than node->data , traverse the left tree
				node = node->left;
			}
			else
			{
				//element is greater than node->data , traverse the right tree
				node = node->right;
			}
		}
		return false;
	}

	//Recurive call to remove the element from the tree
	Node<T>* remove(Node<T>* root, T elem)
	{
		//base condition, node is nullptr
		if (root == nullptr)
		{
			return nullptr;
		}

		Node<T>* node = root;
		Node<T>* temp = nullptr;

		if (elem < node->data)
		{
			//element is less than node->data , traverse the left tree
			node->left = remove(node->left, elem);
		}
		else if(elem > node->data)
		{
			//element is greater than node->data , traverse the right tree
			node->right = remove(node->right, elem);
		}
		else
		{
			//if node's both right and left tree is empty, then delete node
			if (node->left == nullptr && node->right == nullptr)
			{
				delete node;
				node = nullptr;
			}
			//if node's right tree is only empty
			else if (node->right == nullptr )
			{
				temp = node;
				//then assign the node's left tree to node
				node = node->left;
				//delete node
				delete temp;
				temp = nullptr;
			}
			//if node's left tree is only empty
			else if (node->left == nullptr )
			{
				temp = node;
				//then assign the node's right tree to node
				node = node->right;
				//delete node
				delete temp;
				temp = nullptr;

			}
			else
			{
				//if node's both righ and left trees are not nullptr, 
				//find the min value node from right sub tree
				temp = findminnode(node->right);
				
				if (temp)
				{
					//assign the right sub tree min node value to the node->data
					node->data = temp->data;
				}
				//traverse right sub tree to remove the element
				node->right = remove(node->right, elem);
			}

		}
		return node;
	}

	//find min node by traverse through left sub tree
	Node<T>* findminnode(Node<T>* node)
	{
		Node<T>* mindata = nullptr;

		//if node is null
		if (node == nullptr)
			return mindata;

		mindata = node;

		//traverse till leaf node
		while (node != nullptr)
		{
			//node data value is less than mindata value
			if (mindata->data > node->data)
			{
				mindata = node;
			}
			//since finding minimum, traverse through left sub tree
			node = node->left;

		}
		return mindata;
	}

	//find the maximum value by traverse through right sub tree
	Node<T>* findmaxnode(Node<T>* node)
	{
		//node is null
		if (node == nullptr)
			return nullptr;

		Node<T>* maxdata = node;

		//traverse till leaf node
		while (node != nullptr)
		{
			//node data value is greater than maxdata value
			if (maxdata->data < node->data)
			{
				maxdata = node;
			}
			//since finding maximum, traverse through right sub tree
			node = node->right;

		}
		return maxdata;
	}

	//find height of the BST
	int height()
	{
		return height(root);
	}

	//recursive function to find the height of the node
	int height(Node<T>* node)
	{	
		if (node == nullptr)
		{
			return 0;
		}
		else
		{
			//max between right and left node
			return std::max(height(node->left), height(node->right)) + 1;
		}
	}

	//preorder display
	void print_preordertraversal()
	{
		if (root != nullptr)
		{
			Node<T>* node = root;

			//create stack by custom stack using linked list
			liststack<Node<T>*> *stack = new liststack<Node<T>*>();

			//push the root node
			stack->push(node);

			cout << " pre order  ";
			//traverse till stack is empty
			while (!stack->isEmpty())
			{
				//get the top node
				node = stack->top();
				//remove the top node
				stack->pop();
				//print the node value
				cout << node->data << " ";

				//Push the right subtree first
				if (node->right)
				{
					stack->push(node->right);
				}

				//second, push the left subtree
				if (node->left)
				{
					stack->push(node->left);
				}
				//traverse through the left sub tree
				node = node->left;
			}
			delete stack;
		}
		cout << endl;
	}

	void print_inordertraversal()
	{
		if (root != nullptr)
		{
			Node<T>* node = root;

			//create stack by custom stack using linked list
			liststack<Node<T>*>* stack = new liststack<Node<T>*>();

			cout << " in order  ";
			while (!stack->isEmpty() || node !=nullptr)
			{
				//if node is not nullptr, push the node to the stack
				if (node)
				{					
					stack->push(node);
					//traverse through left sub tree
					node = node->left;
				}
				else
				{
					//get the top element
					node = stack->top();

					//remove the top element
					stack->pop();

					//print the data
					cout << node->data << " ";

					//traverse through right sub tree
					node = node->right;
				}
			}
			delete stack;
		}
		cout << endl;
	}
};


template <typename T> class testbinarysearchtree
{
public:
	void testexecution()
	{
		binarysearchtree<int>* bst = new binarysearchtree<int>();

		bst->add(10);
		bst->add(9);
		bst->add(7);
		bst->add(16);
		bst->add(18);
		bst->add(15);
		bst->add(5);
		bst->add(40);
		bst->add(20);

		bst->print_levelordertraversal();
		bst->print_inordertraversal();
		bst->print_preordertraversal();
		bst->print_levelordertraversal();

		cout << "height of the bst === " << bst->height() << endl;

		bst->remove(5);
		bst->remove(9);
		bst->remove(18);
		bst->remove(0);
		bst->print_levelordertraversal();

		cout << endl << endl;
		binarysearchtree<int>* bst1 = new binarysearchtree<int>();

		cout << "bst1 height " << bst1->height() << endl;
		bst1->addrecursive(10);
		cout << "bst1 height " << bst1->height() << endl;
		bst1->addrecursive(5);
		cout << "bst1 height " << bst1->height() << endl;
		bst1->addrecursive(19);
		cout << "bst1 height " << bst1->height() << endl;
		bst1->addrecursive(23);
		bst1->addrecursive(7);
		bst1->print_levelordertraversal();
	}
};

Output: