Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
228 changes: 228 additions & 0 deletions src/main/java/com/thealgorithms/tree/BinarySearchTree.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
package com.thealgorithms.tree;

import java.util.Arrays;

/**
* Binary Search Tree (BST) implementation in Java.
* Supports insertion, traversal (preorder, inorder, postorder),
* balance checking, and pretty display of the tree structure.
*
* Author: Udaya Krishnan M
* GitHub: https://github.com/UdayaKrishnanM/
*/
public class BinarySearchTree {

/**
* Node class representing each element in the BST.
*/
class Node {
private int value;
private Node left;
private Node right;
private int height;

Node(int value) {
this.value = value;
}

int getValue() {
return value;
}

Node getLeft() {
return left;
}

Node getRight() {
return right;
}

int getHeight() {
return height;
}
}

private Node root;

public BinarySearchTree() {
// Empty constructor
}

/**
* Returns the height of a node.
*/
public int height(Node node) {
return node == null ? -1 : node.height;
}

/**
* Returns the root node of the BST.
* Used for testing and internal inspection.
*/
public Node getRoot() {
return root;
}

/**
* Checks if the BST is empty.
*/
public boolean isEmpty() {
return root == null;
}

/**
* Inserts a value into the BST.
*/
public void insert(int value) {
root = createBST(root, value);
}

/**
* Recursively creates the BST based on the value.
*/
public Node createBST(Node node, int value) {
if (node == null) {
return new Node(value);
}
if (value < node.value) {
node.left = createBST(node.left, value);
} else if (value > node.value) {
node.right = createBST(node.right, value);
}
node.height = Math.max(height(node.left), height(node.right)) + 1;
return node;
}

/**
* Populates the BST with an array of values.
*/
public void populate(int[] nums) {
for (int num : nums) {
insert(num);
}
}

/**
* Checks if the BST is balanced.
*/
public boolean balanced() {
return balanced(root);
}

private boolean balanced(Node node) {
if (node == null) {
return true;
}
int balanceFactor = Math.abs(height(node.left) - height(node.right));
System.out.println("Node value: " + node.value + " | Balance Factor: " + balanceFactor);
return balanceFactor <= 1 && balanced(node.left) && balanced(node.right);
}

/**
* Displays the tree in a structured format.
*/
public void prettyDisplay() {
prettyDisplay(root, 0);
}

private void prettyDisplay(Node node, int level) {
if (node == null) {
return;
}
prettyDisplay(node.right, level + 1);
if (level != 0) {
for (int i = 0; i < level - 1; i++) {
System.out.print("|\t");
}
System.out.println("|----> " + node.value);
} else {
System.out.println(node.value);
}
prettyDisplay(node.left, level + 1);
}

/**
* Populates the BST in a balanced way using sorted array.
*/
public void populateSorted(int[] nums) {
Arrays.sort(nums);
populateSorted(nums, 0, nums.length);
}

private void populateSorted(int[] nums, int start, int end) {
if (start >= end) {
return;
}
int mid = start + (end - start) / 2;
insert(nums[mid]);
populateSorted(nums, start, mid);
populateSorted(nums, mid + 1, end);
}

/**
* Preorder traversal: Root -> Left -> Right
*/
public void preOrder() {
preOrder(root);
System.out.println();
}

private void preOrder(Node node) {
if (node == null) {
return;
}
System.out.print(node.value + " ");
preOrder(node.left);
preOrder(node.right);
}

/**
* Inorder traversal: Left -> Root -> Right
*/
public void inOrder() {
inOrder(root);
System.out.println();
}

private void inOrder(Node node) {
if (node == null) {
return;
}
inOrder(node.left);
System.out.print(node.value + " (height: " + node.height + ") | ");
inOrder(node.right);
}

/**
* Postorder traversal: Left -> Right -> Root
*/
public void postOrder() {
postOrder(root);
System.out.println();
}

private void postOrder(Node node) {
if (node == null) {
return;
}
postOrder(node.left);
postOrder(node.right);
System.out.print(node.value + " ");
}

/**
* Displays the tree with node relationships.
*/
public void display() {
display(root, "Root Node: ");
}

private void display(Node node, String details) {
if (node == null) {
return;
}
System.out.println(details + node.value);
display(node.left, "Left child of " + node.value + ": ");
display(node.right, "Right child of " + node.value + ": ");
}
}
166 changes: 166 additions & 0 deletions src/test/java/com/thealgorithms/tree/BinarySearchTree.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package com.thealgorithms.tree;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

/**
* Unit tests for the Binary Search Tree (BST) implementation.
* Covers insertion, traversal, balance checking, and display logic.
*
* Author: Udaya Krishnan M
* GitHub: https://github.com/UdayaKrishnanM/
*/
class BinarySearchTreeTest {

private BinarySearchTree bst;
private final int[] values = {30, 20, 40, 10, 25, 35, 50};

/**
* Initializes the BST before each test with a predefined set of values.
*/
@BeforeEach
void setUp() {
bst = new BinarySearchTree();
bst.populate(values);
}

/**
* Verifies that the BST is not empty after insertion.
*/
@Test
void testBSTIsNotEmpty() {
assertFalse(bst.isEmpty(), "BST should not be empty after insertion");
}

/**
* Tests the height calculation of the root node.
*/
@Test
void testRootHeight() {
int expectedHeight = 2;
assertEquals(expectedHeight, bst.height(bst.getRoot()), "Height of root node should be 2");
}

/**
* Tests the populateSorted method to ensure the BST is balanced
* when populated with a sorted array.
*/
@Test
void testPopulateSortedAndBalanced() {
BinarySearchTree sortedBST = new BinarySearchTree();
int[] sortedArray = {1, 2, 3, 4, 5, 6, 7};
sortedBST.populateSorted(sortedArray);
assertTrue(sortedBST.balanced(), "BST should be balanced after populateSorted");
}

/**
* Unit test for verifying the height calculation of nodes in the BinarySearchTree.
* This test inserts three nodes and checks the height of the left child of the root.
*/
@Test
void testHeightCalculation() {
BinarySearchTree localBST = new BinarySearchTree();
localBST.insert(10); // Root
localBST.insert(5); // Left child
localBST.insert(15); // Right child
assertEquals(0, localBST.height(localBST.getRoot().getLeft()), "Left child height should be 0");
}

/**
* Tests if the BST is balanced.
*/
@Test
void testBalancedTree() {
assertTrue(bst.balanced(), "BST should be balanced with given values");
}

/**
* Tests inorder traversal output.
*/
@Test
void testInOrderTraversal() {
bst.inOrder(); // Output can be redirected and verified if needed
assertTrue(true, "Inorder traversal executed successfully");
}

/**
* Tests preorder traversal output.
*/
@Test
void testPreOrderTraversal() {
bst.preOrder();
assertTrue(true, "Preorder traversal executed successfully");
}

/**
* Tests the height of the root node after inserting multiple values into the BST.
* Ensures the height is calculated correctly for a balanced tree.
*/
@Test
void testRootHeightAfterInsertions() {
bst.populate(new int[] {30, 20, 40, 10, 25, 35, 50});
int expectedHeight = 2;
assertEquals(expectedHeight, bst.height(bst.getRoot()), "Height of root node should be 2");
}

/**
* Tests postorder traversal output.
*/
@Test
void testPostOrderTraversal() {
bst.postOrder();
assertTrue(true, "Postorder traversal executed successfully");
}

/**
* Tests pretty display of the BST.
*/
@Test
void testPrettyDisplay() {
bst.prettyDisplay();
assertTrue(true, "Pretty display executed successfully");
}

/**
* Tests insertion of negative values into the BST.
* Verifies that the tree is not empty and remains balanced.
*/
@Test
void testInsertNegativeValues() {
BinarySearchTree negativeBST = new BinarySearchTree();
int[] negativeValues = {-10, -20, -5, -15};
negativeBST.populate(negativeValues);
assertFalse(negativeBST.isEmpty(), "BST should not be empty after inserting negative values");
assertTrue(negativeBST.balanced(), "BST with negative values should be balanced");
}

/**
* Tests insertion of duplicate values into the BST.
* Verifies that the tree handles duplicates (either inserts or ignores them).
* Note: Current BST implementation inserts duplicates to the right.
*/
@Test
void testInsertDuplicateValues() {
BinarySearchTree duplicateBST = new BinarySearchTree();
int[] valuesWithDuplicates = {10, 20, 10, 30, 20};
duplicateBST.populate(valuesWithDuplicates);
assertFalse(duplicateBST.isEmpty(), "BST should not be empty after inserting duplicates");
duplicateBST.inOrder(); // Visual check if needed
assertTrue(true, "BST handled duplicate values (check logic if duplicates are allowed)");
}

/**
* Tests balanced population using sorted array.
*/
@Test
void testPopulateSorted() {
int[] sortedValues = {10, 20, 30, 40, 50};
BinarySearchTree sortedBST = new BinarySearchTree();
sortedBST.populateSorted(sortedValues);
assertTrue(sortedBST.balanced(), "BST populated with sorted array should be balanced");
}
}