diff --git a/src/main/java/com/thealgorithms/tree/BinarySearchTree.java b/src/main/java/com/thealgorithms/tree/BinarySearchTree.java new file mode 100644 index 000000000000..509a2351a384 --- /dev/null +++ b/src/main/java/com/thealgorithms/tree/BinarySearchTree.java @@ -0,0 +1,242 @@ +package com.thealgorithms.tree; + +import java.util.ArrayList; +import java.util.List; + +/** + * Binary Search Tree (BST) Implementation in Java. + * + * Supports: - insert(key): Insert a key into the BST - delete(key): Delete a + * key from the BST - search(key): Search if a key exists in the BST - + * findMin(): Find the minimum key - findMax(): Find the maximum key - + * inorder(), preorder(), postorder(): Traversals (print) - inorderList(), + * preorderList(), postorderList(): Traversals for testing (return + * List) + * + * Notes: - Duplicate keys are ignored + */ +public class BinarySearchTree { + + /** + * Node class representing each node in BST + */ + private static class Node { + + int key; + Node left; + Node right; + + Node(int key) { + this.key = key; + } + } + + private Node root; + + /** + * Insert a key into the BST + */ + public void insert(int key) { + root = insertRec(root, key); + } + + private Node insertRec(Node root, int key) { + if (root == null) { + return new Node(key); + } + if (key < root.key) { + root.left = insertRec(root.left, key); + }else if (key > root.key) { + root.right = insertRec(root.right, key); + } + return root; // duplicates ignored + } + + /** + * Search for a key in the BST + */ + public boolean search(int key) { + return searchRec(root, key); + } + + private boolean searchRec(Node root, int key) { + if (root == null) { + return false; + } + if (root.key == key) { + return true; + } + return key < root.key ? searchRec(root.left, key) : searchRec(root.right, key); + } + + /** + * Delete a key from the BST + */ + public void delete(int key) { + root = deleteRec(root, key); + } + + private Node deleteRec(Node root, int key) { + if (root == null) { + return null; + } + + if (key < root.key) { + root.left = deleteRec(root.left, key); + }else if (key > root.key) { + root.right = deleteRec(root.right, key); + }else { + // Node found + if (root.left == null && root.right == null) { + return null; // no child + + }if (root.left == null) { + return root.right; // one child + + }if (root.right == null) { + return root.left; // one child + } + // two children: replace with inorder successor + int minValue = findMinRec(root.right); + root.key = minValue; + root.right = deleteRec(root.right, minValue); + } + return root; + } + + /** + * Inorder traversal (print) + */ + public void inorder() { + inorderRec(root); + System.out.println(); + } + + private void inorderRec(Node node) { + if (node != null) { + inorderRec(node.left); + System.out.print(node.key + " "); + inorderRec(node.right); + } + } + + /** + * Preorder traversal (print) + */ + public void preorder() { + preorderRec(root); + System.out.println(); + } + + private void preorderRec(Node node) { + if (node != null) { + System.out.print(node.key + " "); + preorderRec(node.left); + preorderRec(node.right); + } + } + + /** + * Postorder traversal (print) + */ + public void postorder() { + postorderRec(root); + System.out.println(); + } + + private void postorderRec(Node node) { + if (node != null) { + postorderRec(node.left); + postorderRec(node.right); + System.out.print(node.key + " "); + } + } + + /** + * Inorder traversal returning a list (for testing) + */ + public List inorderList() { + List list = new ArrayList<>(); + inorderListRec(root, list); + return list; + } + + private void inorderListRec(Node node, List list) { + if (node == null) { + return; + } + inorderListRec(node.left, list); + list.add(node.key); + inorderListRec(node.right, list); + } + + /** + * Preorder traversal returning a list (for testing) + */ + public List preorderList() { + List list = new ArrayList<>(); + preorderListRec(root, list); + return list; + } + + private void preorderListRec(Node node, List list) { + if (node == null) { + return; + } + list.add(node.key); + preorderListRec(node.left, list); + preorderListRec(node.right, list); + } + + /** + * Postorder traversal returning a list (for testing) + */ + public List postorderList() { + List list = new ArrayList<>(); + postorderListRec(root, list); + return list; + } + + private void postorderListRec(Node node, List list) { + if (node == null) { + return; + } + postorderListRec(node.left, list); + postorderListRec(node.right, list); + list.add(node.key); + } + + /** + * Find minimum key + */ + public int findMin() { + if (root == null) { + throw new IllegalStateException("Tree is empty"); + } + return findMinRec(root); + } + + private int findMinRec(Node node) { + while (node.left != null) { + node = node.left; + } + return node.key; + } + + /** + * Find maximum key + */ + public int findMax() { + if (root == null) { + throw new IllegalStateException("Tree is empty"); + } + return findMaxRec(root); + } + + private int findMaxRec(Node node) { + while (node.right != null) { + node = node.right; + } + return node.key; + } +} diff --git a/src/test/java/com/thealgorithms/tree/BinarySearchTreeTest.java b/src/test/java/com/thealgorithms/tree/BinarySearchTreeTest.java new file mode 100644 index 000000000000..cf0dc79c97d2 --- /dev/null +++ b/src/test/java/com/thealgorithms/tree/BinarySearchTreeTest.java @@ -0,0 +1,87 @@ +package com.thealgorithms.tree; + +import java.util.Arrays; +import java.util.List; + +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 BinarySearchTree. Tests: - insert and search - delete (leaf, + * one child, two children) - findMin and findMax - inorder, preorder, postorder + * traversals + */ +class BinarySearchTreeTest { + + private BinarySearchTree bst; + + @BeforeEach + void setUp() { + bst = new BinarySearchTree(); + bst.insert(10); + bst.insert(5); + bst.insert(15); + } + + @Test + void testSearch() { + assertTrue(bst.search(10)); + assertTrue(bst.search(5)); + assertTrue(bst.search(15)); + assertFalse(bst.search(7)); + } + + @Test + void testFindMinMax() { + assertEquals(5, bst.findMin()); + assertEquals(15, bst.findMax()); + } + + @Test + void testDeleteLeafNode() { + bst.delete(5); + assertFalse(bst.search(5)); + assertTrue(bst.search(10)); + assertTrue(bst.search(15)); + } + + @Test + void testDeleteNodeWithOneChild() { + bst.insert(12); + bst.delete(15); + assertFalse(bst.search(15)); + assertTrue(bst.search(12)); + } + + @Test + void testDeleteNodeWithTwoChildren() { + bst.insert(12); + bst.insert(20); + bst.delete(10); + assertFalse(bst.search(10)); + assertTrue(bst.search(5)); + assertTrue(bst.search(12)); + assertTrue(bst.search(15)); + } + + @Test + void testInorderTraversal() { + List expected = Arrays.asList(5, 10, 15); + assertEquals(expected, bst.inorderList()); + } + + @Test + void testPreorderTraversal() { + List expected = Arrays.asList(10, 5, 15); + assertEquals(expected, bst.preorderList()); + } + + @Test + void testPostorderTraversal() { + List expected = Arrays.asList(5, 15, 10); + assertEquals(expected, bst.postorderList()); + } +}