Implementing a Binary Search Tree
This article provides a Java-based implementation of a Binary Search Tree (BST), building upon the theoretical concepts discussed previously.
First, a Node class is defined to represent each element within the tree. It encapsulates the node’s value and references to its left and right children.
/**
* Represents a node in the Binary Search Tree.
*/
class Node {
int value;
Node leftChild;
Node rightChild;
public Node(int value) {
this.value = value;
this.leftChild = null;
this.rightChild = null;
}
}
The BinaryTree class implements the core BST operations: insertion, deletion, and the standard traversal algorithms (pre-order, in-order, and post-order).
/**
* Implements a Binary Search Tree.
*/
class BinaryTree {
Node rootNode = null;
/**
* Inserts a new element into the BST.
* The new node is placed based on the BST property.
*/
public void insertNode(int value) {
if (rootNode == null) {
rootNode = new Node(value);
return;
}
Node currentNode = rootNode;
while (true) {
if (value < currentNode.value) {
if (currentNode.leftChild == null) {
currentNode.leftChild = new Node(value);
return;
}
currentNode = currentNode.leftChild;
} else {
if (currentNode.rightChild == null) {
currentNode.rightChild = new Node(value);
return;
}
currentNode = currentNode.rightChild;
}
}
}
/**
* Removes a node with the specified value from the BST.
* Returns true if the node is found and removed, false otherwise.
*/
public boolean removeNode(int value) {
Node currentNode = rootNode;
Node parentNode = null;
// Find the node to remove
while (currentNode != null && currentNode.value != value) {
parentNode = currentNode;
if (value < currentNode.value) {
currentNode = currentNode.leftChild;
} else {
currentNode = currentNode.rightChild;
}
}
if (currentNode == null) {
// Node not found
return false;
}
// Case 1: Node to be deleted has no children (it is a leaf node)
if (currentNode.leftChild == null && currentNode.rightChild == null) {
if (currentNode == rootNode) {
rootNode = null;
} else if (currentNode == parentNode.leftChild) {
parentNode.leftChild = null;
} else {
parentNode.rightChild = null;
}
}
// Case 2: Node to be deleted has only one child
else if (currentNode.rightChild == null) { // Has only left child
if (currentNode == rootNode) {
rootNode = currentNode.leftChild;
} else if (currentNode == parentNode.leftChild) {
parentNode.leftChild = currentNode.leftChild;
} else {
parentNode.rightChild = currentNode.leftChild;
}
} else if (currentNode.leftChild == null) { // Has only right child
if (currentNode == rootNode) {
rootNode = currentNode.rightChild;
} else if (currentNode == parentNode.leftChild) {
parentNode.leftChild = currentNode.rightChild;
} else {
parentNode.rightChild = currentNode.rightChild;
}
}
// Case 3: Node to be deleted has two children
else {
Node successor = getSuccessor(currentNode);
if (currentNode == rootNode) {
rootNode = successor;
} else if (currentNode == parentNode.leftChild) {
parentNode.leftChild = successor;
} else {
parentNode.rightChild = successor;
}
successor.leftChild = currentNode.leftChild;
}
return true;
}
/**
* Helper method to find the successor node for a node with two children.
* The successor is the smallest node in the right subtree.
*/
private Node getSuccessor(Node nodeToDelete) {
Node successorParent = nodeToDelete;
Node successor = nodeToDelete.rightChild;
Node current = successor.leftChild;
while (current != null) {
successorParent = successor;
successor = current;
current = current.leftChild;
}
if (successor != nodeToDelete.rightChild) {
successorParent.leftChild = successor.rightChild;
successor.rightChild = nodeToDelete.rightChild;
}
return successor;
}
/**
* Performs in-order traversal of the tree.
*/
public void inorderTree(Node root, int depth) {
if (root != null) {
inorderTree(root.leftChild, depth + 1);
printNode(root, depth);
inorderTree(root.rightChild, depth + 1);
}
}
/**
* Performs post-order traversal of the tree.
*/
public void postorderTree(Node root, int depth) {
if (root != null) {
postorderTree(root.leftChild, depth + 1);
postorderTree(root.rightChild, depth + 1);
printNode(root, depth);
}
}
/**
* Performs pre-order traversal of the tree.
*/
public void preorderTree(Node root, int depth) {
if (root != null) {
printNode(root, depth);
preorderTree(root.leftChild, depth + 1);
preorderTree(root.rightChild, depth + 1);
}
}
private void printNode(Node node, int depth) {
for (int i = 0; i < depth; i++) {
System.out.print(" ");
}
System.out.println(node.value);
}
}
Finally, the execution class demonstrates the usage of the BinaryTree.
/**
* Test class for the BinarySearchTree implementation.
*/
class BinarySearchTreeTest {
public static void main(String[] args) {
BinaryTree tree = new BinaryTree();
tree.insertNode(5);
tree.insertNode(8);
tree.insertNode(7);
tree.insertNode(10);
tree.insertNode(9);
tree.insertNode(11);
if (tree.removeNode(10)) {
System.out.println("Node 10 removed successfully.");
}
System.out.println("\nPre-order Traversal:");
tree.preorderTree(tree.rootNode, 0);
// System.out.println("\nIn-order Traversal:");
// tree.inorderTree(tree.rootNode, 0);
// System.out.println("\nPost-order Traversal:");
// tree.postorderTree(tree.rootNode, 0);
}
}