Binary Search Tree

Dec 27, 2022 · 6 mins read
Binary Search Tree

Some Basic Concepts

A binary search tree (BST) is a data structure that is used to store data in a sorted and searchable manner. It consists of nodes organized in a tree-like structure, where each node has at most two children. The left child of a node contains a value that is less than the value of the parent node, and the right child contains a value that is greater than the value of the parent node. This means that the values of the nodes in the left subtree of a node are all less than the value of the node, and the values of the nodes in the right subtree are all greater than the value of the node.

Binary search trees have the following properties:

  • The left subtree of a node contains only nodes with keys less than the node’s key.
  • The right subtree of a node contains only nodes with keys greater than the node’s key.
  • The left and right subtree each must also be a binary search tree. Binary search trees are often used for fast data insertion, deletion, and search operations, as the time complexity of these operations is O(log n) in the average case and O(n) in the worst case, where n is the number of nodes in the tree.

Some common operations that can be performed on a binary search tree include inserting a new node, deleting a node, searching for a particular value, and traversing the tree in different orders (such as in-order, pre-order, and post-order)

We are now going to see different kind of operations in Binary Search Tree. So let’s get straight into the code. I am using C++ for these operations.

Code

#include <iostream>
using namespace std;
class Node {
public:
  int data;
  Node *left;
  Node *right;

  // Constructor
  Node(int data) {
    this->data = data;
    this->left = NULL;
    this->right = NULL;
  }
};

Node *insert(Node* &root, int data) {
  if (root == NULL) {
    root = new Node(data);
    return root;
  }
  if (data > root->data) {
    root->right = insert(root->right, data);
  } else {
    root->left = insert(root->left, data);
  }
  return root;
}
// Inorder Traversal gives us sorted order list for BST
void inorder(Node *root) {
  if (root == NULL) {
    return;
  }
  inorder(root->left);
  cout << root->data << " ";
  inorder(root->right);
}
void preorder(Node *root) {
  if (root == NULL) {
    return;
  }
  cout << root->data << " ";
  inorder(root->left);
  inorder(root->right);
}
void postorder(Node *root) {
  if (root == NULL) {
    return;
  }
  postorder(root->left);
  postorder(root->right);
  cout << root->data << " ";
}
int height(Node *root) {
  if (root == NULL) {
    return 0;
  }
  int lHeight = height(root->left);
  int rHeight = height(root->right);

  return max(lHeight, rHeight) + 1;
}
bool search(Node *root, int key) {
  bool ans = false;
  if (root == NULL) {
    return ans;
  }
  // Data exist condition
  if (key == root->data) {
    cout << "Data Found!!" << endl;
    return true;
  }
  if (key > root->data) {
    search(root->right, key);
  } else {
    search(root->left, key);
  }
  return ans;
}

int smallest(Node *root) {
  // simple concept
  while (root->left != NULL) {
    root = root->left;
  }
  return root->data;
}
int largest(Node *root) {
  // simple concept
  while (root->right != NULL) {
    root = root->right;
  }
  return root->data;
}
int totalNodes(Node *root) {
  if (root == NULL) {
    return 0;
  }
  int lNodes = totalNodes(root->left);
  int rNodes = totalNodes(root->right);

  return lNodes + rNodes + 1;
}
// The nodes which are not leaf nodes -> Internal Nodes
int totalInternalNode(Node *root) {
  if (root == NULL) {
    return 0;
  }
  if (root->left == NULL && root->right == NULL) {
    return 0;
  }
  return (totalInternalNode(root->left) + totalInternalNode(root->right) + 1);
}
// The nodes which are leaf nodes -> External Nodes
int totalExternalNode(Node *root) {
  // Simple Solution
  // return (totalNodes(root)-totalInternalNode(root));

  // Complex Solution or Traditional Solution
  if (root == NULL) {
    return 0;
  }
  if (root->left == NULL && root->right == NULL) {
    return 1;
  }
  return (totalExternalNode(root->left) + totalExternalNode(root->right));
}
int sumOfNodes(Node *root) {
  if (root == NULL) {
    return 0;
  }
  return (sumOfNodes(root->right) + sumOfNodes(root->left) + root->data);
}

Node* deleteFromBST(Node* root, int data){
	//Base case
	if(root == NULL){
		return root;
	}
	if(root->data == data){
		// Main deletion operation will be handled here
		// Here we will handle 3 cases

		// Case 1: 0 Child
		if(root->right==NULL && root->left==NULL){
			// Perform delete to free the memory
			delete root;
			// Make that node points to NULL
			return NULL;
		}
		// Case 2: 1 Child
		// Here comes two sub-cases
		// Sub-Case 1: Right Child Exists
		if(root->right != NULL && root->left == NULL){
			// Store the right child 
			Node* temp = root->right;
			// Perform delete
			delete root;
			// Return the right child 
			return temp;
		}
		// Sub-Case 2: Left Child Exists
		if(root->right == NULL && root->left != NULL){
			// Store the left child 
			Node* temp = root->left;
			// Perform delete
			delete root;
			// Return the right child 
			return temp;
		}
		// Case 3: 2 Children
		if(root->left != NULL && root->right != NULL){
			// Step No. 1: We have to find the minimum value of right subtree
			// Or we can find out the maximum value of left subtree
			// any one of these will work fine. I will go for the first one.
	
			// Find the minimum value from right subtree
			int minValue = smallest(root->right);
			// Step No. 2: Copy that value to the present node
			root->data = minValue;
			//  Step No. 3: 
			// Now the data is successfully copied so we don't need that node
			// That's why we need to delete it from the right subtree because
			// we know that it is present on the right subtree
			root->right = deleteFromBST(root->right,minValue);
			// Step No.4: Now return the root for final operation
			return root;
		}
	}
	// If value is greater than root->data
	else if(data > root->data){
		//We are calling the function for the right sub-tree
		root->right = deleteFromBST(root->right, data);
		return root;
	}
	// If value is lesser than root->data
	else{
		//We are calling the function for the left sub-tree
		root->left = deleteFromBST(root->left, data);
		return root;
	}
}

int main() {
  /*

                               6
                             /   \
                            4	  9
                          /  \     \
                         1	  5	   11
  */

  Node *root = NULL;
  insert(root, 6);
  insert(root, 4);
  insert(root, 9);
  insert(root, 1);
  insert(root, 5);
  insert(root, 11);
  inorder(root);
  cout << endl;
  preorder(root);
  cout << endl;
  postorder(root);
  cout << endl;
  cout << "Height of the tree is: " << height(root) << endl;
  search(root, 4);
  if (!search(root, 10)) {
    cout << "Data doesn't exist!!" << endl;
  }
  cout << smallest(root) << endl;
  cout << largest(root) << endl;
  cout << totalNodes(root) << endl;
  cout << totalInternalNode(root) << endl;
  cout << totalExternalNode(root) << endl;
  cout << sumOfNodes(root) << endl;
  root = deleteFromBST(root, 4);
  inorder(root);
  cout<<endl;
  cout<<totalNodes(root)<<endl;
}

Output

1 4 5 6 9 11 
6 1 4 5 9 11 
1 5 4 11 9 6 
Height of the tree is: 3
Data Found!!
Data doesn't exist!!
1
11
6
3
3
36
1 5 6 9 11 
5

Sharing is caring!