From 7bc6d40f12fd6de921f8dc829724ef507d0ac3ee Mon Sep 17 00:00:00 2001 From: Raghu0703 <2400030362@kluniversity.in> Date: Sun, 23 Nov 2025 23:30:39 +0530 Subject: [PATCH 1/5] feat: Add Binary Search Tree (BST) and AVL Tree implementations - Implemented BST with insert, delete, search operations - Implemented AVL Tree with self-balancing rotations - Added comprehensive unit tests - Fixes #6957 --- .../datastructures/trees/AVLTree.java | 565 +++++++++++------- .../trees/BinarySearchTree.java | 246 ++++++++ .../datastructures/trees/AVLTreeTest.java | 416 ++++++++++--- .../trees/BinarySearchTreeTest.java | 242 ++++++++ 4 files changed, 1169 insertions(+), 300 deletions(-) create mode 100644 src/main/java/com/thealgorithms/datastructures/trees/BinarySearchTree.java create mode 100644 src/test/java/com/thealgorithms/datastructures/trees/BinarySearchTreeTest.java diff --git a/src/main/java/com/thealgorithms/datastructures/trees/AVLTree.java b/src/main/java/com/thealgorithms/datastructures/trees/AVLTree.java index 77ee5d5fa23e..553c63c847d5 100644 --- a/src/main/java/com/thealgorithms/datastructures/trees/AVLTree.java +++ b/src/main/java/com/thealgorithms/datastructures/trees/AVLTree.java @@ -1,269 +1,388 @@ package com.thealgorithms.datastructures.trees; -import java.util.ArrayList; -import java.util.List; - /** - * Represents an AVL Tree, a self-balancing binary search tree. - * In an AVL tree, the heights of the two child subtrees of any node - * differ by at most one. If they differ by more than one at any time, - * rebalancing is performed to restore this property. + * AVL Tree (Adelson-Velsky and Landis Tree) implementation. + * A self-balancing Binary Search Tree where the difference between heights + * of left and right subtrees cannot be more than one for all nodes. + * + * @author Raghu0703 */ public class AVLTree { - - private Node root; - - private static class Node { - private int key; - private int balance; - private int height; - private Node left; - private Node right; - private Node parent; - - Node(int k, Node p) { - key = k; - parent = p; - } - - public Integer getBalance() { - return balance; + + /** + * Node class representing each element in the AVL Tree + */ + class Node { + int data; + int height; + Node left, right; + + public Node(int data) { + this.data = data; + this.height = 1; + this.left = null; + this.right = null; } } - + + private Node root; + /** - * Inserts a new key into the AVL tree. - * - * @param key the key to be inserted - * @return {@code true} if the key was inserted, {@code false} if the key already exists + * Constructor to initialize empty AVL Tree */ - public boolean insert(int key) { - if (root == null) { - root = new Node(key, null); - } else { - Node n = root; - Node parent; - while (true) { - if (n.key == key) { - return false; - } - - parent = n; - boolean goLeft = n.key > key; - n = goLeft ? n.left : n.right; - - if (n == null) { - if (goLeft) { - parent.left = new Node(key, parent); - } else { - parent.right = new Node(key, parent); - } - rebalance(parent); - break; - } - } - } - return true; + public AVLTree() { + this.root = null; } - + /** - * Deletes a key from the AVL tree. - * - * @param delKey the key to be deleted + * Get the height of a node + * + * @param node the node + * @return height of the node, 0 if null */ - public void delete(int delKey) { - if (root == null) { - return; + private int height(Node node) { + if (node == null) { + return 0; } - - // Find the node to be deleted - Node node = root; - Node child = root; - while (child != null) { - node = child; - child = delKey >= node.key ? node.right : node.left; - if (delKey == node.key) { - delete(node); - return; - } + return node.height; + } + + /** + * Get the balance factor of a node + * + * @param node the node + * @return balance factor (left height - right height) + */ + private int getBalance(Node node) { + if (node == null) { + return 0; } + return height(node.left) - height(node.right); } - - private void delete(Node node) { - if (node.left == null && node.right == null) { - // Leaf node - if (node.parent == null) { - root = null; - } else { - Node parent = node.parent; - if (parent.left == node) { - parent.left = null; - } else { - parent.right = null; - } - rebalance(parent); - } - return; + + /** + * Right rotate subtree rooted with y + * + * @param y the root of subtree to rotate + * @return new root after rotation + */ + private Node rightRotate(Node y) { + Node x = y.left; + Node T2 = x.right; + + // Perform rotation + x.right = y; + y.left = T2; + + // Update heights + y.height = Math.max(height(y.left), height(y.right)) + 1; + x.height = Math.max(height(x.left), height(x.right)) + 1; + + return x; + } + + /** + * Left rotate subtree rooted with x + * + * @param x the root of subtree to rotate + * @return new root after rotation + */ + private Node leftRotate(Node x) { + Node y = x.right; + Node T2 = y.left; + + // Perform rotation + y.left = x; + x.right = T2; + + // Update heights + x.height = Math.max(height(x.left), height(x.right)) + 1; + y.height = Math.max(height(y.left), height(y.right)) + 1; + + return y; + } + + /** + * Insert a value into the AVL Tree + * + * @param data the value to insert + */ + public void insert(int data) { + root = insertRec(root, data); + } + + /** + * Recursive helper method to insert a value and balance the tree + * + * @param node current node + * @param data value to insert + * @return the balanced node + */ + private Node insertRec(Node node, int data) { + // Perform normal BST insertion + if (node == null) { + return new Node(data); } - - // Node has one or two children - Node child; - if (node.left != null) { - child = node.left; - while (child.right != null) { - child = child.right; - } + + if (data < node.data) { + node.left = insertRec(node.left, data); + } else if (data > node.data) { + node.right = insertRec(node.right, data); } else { - child = node.right; - while (child.left != null) { - child = child.left; - } + // Duplicate values not allowed + return node; + } + + // Update height of current node + node.height = 1 + Math.max(height(node.left), height(node.right)); + + // Get balance factor + int balance = getBalance(node); + + // If node is unbalanced, there are 4 cases + + // Left Left Case + if (balance > 1 && data < node.left.data) { + return rightRotate(node); + } + + // Right Right Case + if (balance < -1 && data > node.right.data) { + return leftRotate(node); } - node.key = child.key; - delete(child); + + // Left Right Case + if (balance > 1 && data > node.left.data) { + node.left = leftRotate(node.left); + return rightRotate(node); + } + + // Right Left Case + if (balance < -1 && data < node.right.data) { + node.right = rightRotate(node.right); + return leftRotate(node); + } + + return node; } - + /** - * Returns a list of balance factors for each node in the tree. - * - * @return a list of integers representing the balance factors of the nodes + * Find the node with minimum value + * + * @param node the root of subtree + * @return node with minimum value */ - public List returnBalance() { - List balances = new ArrayList<>(); - returnBalance(root, balances); - return balances; - } - - private void returnBalance(Node n, List balances) { - if (n != null) { - returnBalance(n.left, balances); - balances.add(n.getBalance()); - returnBalance(n.right, balances); + private Node minValueNode(Node node) { + Node current = node; + while (current.left != null) { + current = current.left; } + return current; } - + /** - * Searches for a key in the AVL tree. - * - * @param key the key to be searched - * @return true if the key is found, false otherwise + * Delete a value from the AVL Tree + * + * @param data the value to delete */ - public boolean search(int key) { - Node result = searchHelper(this.root, key); - return result != null; + public void delete(int data) { + root = deleteRec(root, data); } - - private Node searchHelper(Node root, int key) { - if (root == null || root.key == key) { + + /** + * Recursive helper method to delete a value and balance the tree + * + * @param root current node + * @param data value to delete + * @return the balanced node + */ + private Node deleteRec(Node root, int data) { + // Perform standard BST delete + if (root == null) { return root; } - - if (root.key > key) { - return searchHelper(root.left, key); - } - return searchHelper(root.right, key); - } - - private void rebalance(Node n) { - setBalance(n); - if (n.balance == -2) { - if (height(n.left.left) >= height(n.left.right)) { - n = rotateRight(n); - } else { - n = rotateLeftThenRight(n); - } - } else if (n.balance == 2) { - if (height(n.right.right) >= height(n.right.left)) { - n = rotateLeft(n); + + if (data < root.data) { + root.left = deleteRec(root.left, data); + } else if (data > root.data) { + root.right = deleteRec(root.right, data); + } else { + // Node with only one child or no child + if ((root.left == null) || (root.right == null)) { + Node temp = root.left != null ? root.left : root.right; + + // No child case + if (temp == null) { + temp = root; + root = null; + } else { + // One child case + root = temp; + } } else { - n = rotateRightThenLeft(n); + // Node with two children + Node temp = minValueNode(root.right); + root.data = temp.data; + root.right = deleteRec(root.right, temp.data); } } - - if (n.parent != null) { - rebalance(n.parent); - } else { - root = n; + + // If tree had only one node + if (root == null) { + return root; } - } - - private Node rotateLeft(Node a) { - Node b = a.right; - b.parent = a.parent; - - a.right = b.left; - - if (a.right != null) { - a.right.parent = a; + + // Update height + root.height = Math.max(height(root.left), height(root.right)) + 1; + + // Get balance factor + int balance = getBalance(root); + + // If unbalanced, there are 4 cases + + // Left Left Case + if (balance > 1 && getBalance(root.left) >= 0) { + return rightRotate(root); } - - b.left = a; - a.parent = b; - - if (b.parent != null) { - if (b.parent.right == a) { - b.parent.right = b; - } else { - b.parent.left = b; - } + + // Left Right Case + if (balance > 1 && getBalance(root.left) < 0) { + root.left = leftRotate(root.left); + return rightRotate(root); } - - setBalance(a, b); - return b; - } - - private Node rotateRight(Node a) { - Node b = a.left; - b.parent = a.parent; - - a.left = b.right; - - if (a.left != null) { - a.left.parent = a; + + // Right Right Case + if (balance < -1 && getBalance(root.right) <= 0) { + return leftRotate(root); } - - b.right = a; - a.parent = b; - - if (b.parent != null) { - if (b.parent.right == a) { - b.parent.right = b; - } else { - b.parent.left = b; - } + + // Right Left Case + if (balance < -1 && getBalance(root.right) > 0) { + root.right = rightRotate(root.right); + return leftRotate(root); } - - setBalance(a, b); - return b; + + return root; } - - private Node rotateLeftThenRight(Node n) { - n.left = rotateLeft(n.left); - return rotateRight(n); + + /** + * Search for a value in the AVL Tree + * + * @param data the value to search for + * @return true if found, false otherwise + */ + public boolean search(int data) { + return searchRec(root, data); } - - private Node rotateRightThenLeft(Node n) { - n.right = rotateRight(n.right); - return rotateLeft(n); + + /** + * Recursive helper method to search for a value + * + * @param root current node + * @param data value to search for + * @return true if found, false otherwise + */ + private boolean searchRec(Node root, int data) { + if (root == null) { + return false; + } + + if (root.data == data) { + return true; + } + + if (data < root.data) { + return searchRec(root.left, data); + } + + return searchRec(root.right, data); } - - private int height(Node n) { - if (n == null) { - return -1; + + /** + * Inorder traversal (Left-Root-Right) + * + * @return string representation of inorder traversal + */ + public String inorder() { + StringBuilder result = new StringBuilder(); + inorderRec(root, result); + return result.toString().trim(); + } + + /** + * Recursive helper for inorder traversal + */ + private void inorderRec(Node root, StringBuilder result) { + if (root != null) { + inorderRec(root.left, result); + result.append(root.data).append(" "); + inorderRec(root.right, result); } - return n.height; } - - private void setBalance(Node... nodes) { - for (Node n : nodes) { - reheight(n); - n.balance = height(n.right) - height(n.left); + + /** + * Preorder traversal (Root-Left-Right) + * + * @return string representation of preorder traversal + */ + public String preorder() { + StringBuilder result = new StringBuilder(); + preorderRec(root, result); + return result.toString().trim(); + } + + /** + * Recursive helper for preorder traversal + */ + private void preorderRec(Node root, StringBuilder result) { + if (root != null) { + result.append(root.data).append(" "); + preorderRec(root.left, result); + preorderRec(root.right, result); } } - - private void reheight(Node node) { - if (node != null) { - node.height = 1 + Math.max(height(node.left), height(node.right)); + + /** + * Get the height of the tree + * + * @return the height of the tree + */ + public int getHeight() { + return height(root); + } + + /** + * Check if the tree is empty + * + * @return true if empty, false otherwise + */ + public boolean isEmpty() { + return root == null; + } + + /** + * Check if the tree is balanced (for testing purposes) + * + * @return true if balanced, false otherwise + */ + public boolean isBalanced() { + return isBalancedRec(root); + } + + /** + * Recursive helper to check if tree is balanced + */ + private boolean isBalancedRec(Node node) { + if (node == null) { + return true; + } + + int balance = getBalance(node); + + if (Math.abs(balance) > 1) { + return false; } + + return isBalancedRec(node.left) && isBalancedRec(node.right); } } diff --git a/src/main/java/com/thealgorithms/datastructures/trees/BinarySearchTree.java b/src/main/java/com/thealgorithms/datastructures/trees/BinarySearchTree.java new file mode 100644 index 000000000000..ee6f1b479235 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/BinarySearchTree.java @@ -0,0 +1,246 @@ +package com.thealgorithms.datastructures.trees; + +/** + * Binary Search Tree implementation with insert, search, delete, and traversal operations. + * + * @author Raghu0703 + */ +public class BinarySearchTree { + + /** + * Node class representing each element in the BST + */ + class Node { + int data; + Node left, right; + + public Node(int data) { + this.data = data; + this.left = null; + this.right = null; + } + } + + private Node root; + + /** + * Constructor to initialize empty BST + */ + public BinarySearchTree() { + this.root = null; + } + + /** + * Insert a value into the BST + * + * @param data the value to insert + */ + public void insert(int data) { + root = insertRec(root, data); + } + + /** + * Recursive helper method to insert a value + * + * @param root current node + * @param data value to insert + * @return the modified node + */ + private Node insertRec(Node root, int data) { + if (root == null) { + root = new Node(data); + return root; + } + + if (data < root.data) { + root.left = insertRec(root.left, data); + } else if (data > root.data) { + root.right = insertRec(root.right, data); + } + + return root; + } + + /** + * Search for a value in the BST + * + * @param data the value to search for + * @return true if found, false otherwise + */ + public boolean search(int data) { + return searchRec(root, data); + } + + /** + * Recursive helper method to search for a value + * + * @param root current node + * @param data value to search for + * @return true if found, false otherwise + */ + private boolean searchRec(Node root, int data) { + if (root == null) { + return false; + } + + if (root.data == data) { + return true; + } + + if (data < root.data) { + return searchRec(root.left, data); + } + + return searchRec(root.right, data); + } + + /** + * Delete a value from the BST + * + * @param data the value to delete + */ + public void delete(int data) { + root = deleteRec(root, data); + } + + /** + * Recursive helper method to delete a value + * + * @param root current node + * @param data value to delete + * @return the modified node + */ + private Node deleteRec(Node root, int data) { + if (root == null) { + return root; + } + + if (data < root.data) { + root.left = deleteRec(root.left, data); + } else if (data > root.data) { + root.right = deleteRec(root.right, data); + } else { + // Node with only one child or no child + if (root.left == null) { + return root.right; + } else if (root.right == null) { + return root.left; + } + + // Node with two children: Get inorder successor + root.data = minValue(root.right); + root.right = deleteRec(root.right, root.data); + } + + return root; + } + + /** + * Find the minimum value in a subtree + * + * @param root the root of the subtree + * @return the minimum value + */ + private int minValue(Node root) { + int minValue = root.data; + while (root.left != null) { + minValue = root.left.data; + root = root.left; + } + return minValue; + } + + /** + * Inorder traversal (Left-Root-Right) + * + * @return string representation of inorder traversal + */ + public String inorder() { + StringBuilder result = new StringBuilder(); + inorderRec(root, result); + return result.toString().trim(); + } + + /** + * Recursive helper for inorder traversal + */ + private void inorderRec(Node root, StringBuilder result) { + if (root != null) { + inorderRec(root.left, result); + result.append(root.data).append(" "); + inorderRec(root.right, result); + } + } + + /** + * Preorder traversal (Root-Left-Right) + * + * @return string representation of preorder traversal + */ + public String preorder() { + StringBuilder result = new StringBuilder(); + preorderRec(root, result); + return result.toString().trim(); + } + + /** + * Recursive helper for preorder traversal + */ + private void preorderRec(Node root, StringBuilder result) { + if (root != null) { + result.append(root.data).append(" "); + preorderRec(root.left, result); + preorderRec(root.right, result); + } + } + + /** + * Postorder traversal (Left-Right-Root) + * + * @return string representation of postorder traversal + */ + public String postorder() { + StringBuilder result = new StringBuilder(); + postorderRec(root, result); + return result.toString().trim(); + } + + /** + * Recursive helper for postorder traversal + */ + private void postorderRec(Node root, StringBuilder result) { + if (root != null) { + postorderRec(root.left, result); + postorderRec(root.right, result); + result.append(root.data).append(" "); + } + } + + /** + * Get the height of the tree + * + * @return the height of the tree + */ + public int height() { + return heightRec(root); + } + + /** + * Recursive helper to calculate height + */ + private int heightRec(Node root) { + if (root == null) { + return 0; + } + return 1 + Math.max(heightRec(root.left), heightRec(root.right)); + } + + /** + * Check if the tree is empty + * + * @return true if empty, false otherwise + */ + public boolean isEmpty() { + return root == null; + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/AVLTreeTest.java b/src/test/java/com/thealgorithms/datastructures/trees/AVLTreeTest.java index 6aa5dc9e22ed..6bd14e25f2d1 100644 --- a/src/test/java/com/thealgorithms/datastructures/trees/AVLTreeTest.java +++ b/src/test/java/com/thealgorithms/datastructures/trees/AVLTreeTest.java @@ -1,101 +1,363 @@ package com.thealgorithms.datastructures.trees; -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 java.util.List; +import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -public class AVLTreeTest { - private AVLTree avlTree; - +/** + * Test class for AVLTree + * + * @author Raghu0703 + */ +class AVLTreeTest { + + private AVLTree avl; + @BeforeEach - public void setUp() { - avlTree = new AVLTree(); + void setUp() { + avl = new AVLTree(); } - + @Test - public void testInsert() { - assertTrue(avlTree.insert(10)); - assertTrue(avlTree.insert(20)); - assertTrue(avlTree.insert(5)); - assertFalse(avlTree.insert(10)); // Duplicate + void testEmptyTree() { + assertTrue(avl.isEmpty(), "New tree should be empty"); + assertEquals(0, avl.getHeight(), "Empty tree height should be 0"); + assertTrue(avl.isBalanced(), "Empty tree should be balanced"); } - + @Test - public void testSearch() { - avlTree.insert(15); - avlTree.insert(25); - assertTrue(avlTree.search(15)); - assertFalse(avlTree.search(30)); // Not in the tree + void testInsertSingleElement() { + avl.insert(10); + assertFalse(avl.isEmpty(), "Tree should not be empty after insert"); + assertTrue(avl.search(10), "Should find inserted element"); + assertEquals(1, avl.getHeight(), "Height should be 1 with single element"); + assertTrue(avl.isBalanced(), "Tree should be balanced"); } - + @Test - public void testDeleteLeafNode() { - avlTree.insert(10); - avlTree.insert(20); - avlTree.insert(30); - avlTree.delete(30); - assertFalse(avlTree.search(30)); + void testInsertMultipleElements() { + avl.insert(50); + avl.insert(30); + avl.insert(70); + avl.insert(20); + avl.insert(40); + avl.insert(60); + avl.insert(80); + + assertTrue(avl.search(50), "Should find 50"); + assertTrue(avl.search(30), "Should find 30"); + assertTrue(avl.search(70), "Should find 70"); + assertTrue(avl.search(20), "Should find 20"); + assertTrue(avl.search(40), "Should find 40"); + assertTrue(avl.search(60), "Should find 60"); + assertTrue(avl.search(80), "Should find 80"); + assertTrue(avl.isBalanced(), "Tree should remain balanced"); } - + @Test - public void testDeleteNodeWithOneChild() { - avlTree.insert(20); - avlTree.insert(10); - avlTree.insert(30); - avlTree.delete(10); - assertFalse(avlTree.search(10)); + void testLeftLeftRotation() { + // Insert in descending order to trigger LL rotation + avl.insert(30); + avl.insert(20); + avl.insert(10); + + assertEquals("10 20 30", avl.inorder(), "Tree should be balanced after LL rotation"); + assertTrue(avl.isBalanced(), "Tree should be balanced"); + assertEquals(2, avl.getHeight(), "Height should be 2 after balancing"); } - + @Test - public void testDeleteNodeWithTwoChildren() { - avlTree.insert(20); - avlTree.insert(10); - avlTree.insert(30); - avlTree.insert(25); - avlTree.delete(20); - assertFalse(avlTree.search(20)); - assertTrue(avlTree.search(30)); - assertTrue(avlTree.search(25)); + void testRightRightRotation() { + // Insert in ascending order to trigger RR rotation + avl.insert(10); + avl.insert(20); + avl.insert(30); + + assertEquals("10 20 30", avl.inorder(), "Tree should be balanced after RR rotation"); + assertTrue(avl.isBalanced(), "Tree should be balanced"); + assertEquals(2, avl.getHeight(), "Height should be 2 after balancing"); } - + @Test - public void testReturnBalance() { - avlTree.insert(10); - avlTree.insert(20); - avlTree.insert(5); - List balances = avlTree.returnBalance(); - assertEquals(3, balances.size()); // There should be 3 nodes - assertEquals(0, balances.get(0)); // Balance for node 5 - assertEquals(0, balances.get(1)); // Balance for node 10 - assertEquals(0, balances.get(2)); // Balance for node 20 + void testLeftRightRotation() { + // Insert to trigger LR rotation + avl.insert(30); + avl.insert(10); + avl.insert(20); + + assertEquals("10 20 30", avl.inorder(), "Tree should be balanced after LR rotation"); + assertTrue(avl.isBalanced(), "Tree should be balanced"); + assertEquals(2, avl.getHeight(), "Height should be 2 after balancing"); } - + @Test - public void testInsertAndRebalance() { - avlTree.insert(30); - avlTree.insert(20); - avlTree.insert(10); // This should cause a right rotation - assertTrue(avlTree.search(20)); - assertTrue(avlTree.search(10)); - assertTrue(avlTree.search(30)); + void testRightLeftRotation() { + // Insert to trigger RL rotation + avl.insert(10); + avl.insert(30); + avl.insert(20); + + assertEquals("10 20 30", avl.inorder(), "Tree should be balanced after RL rotation"); + assertTrue(avl.isBalanced(), "Tree should be balanced"); + assertEquals(2, avl.getHeight(), "Height should be 2 after balancing"); } - + @Test - public void testComplexInsertionAndDeletion() { - avlTree.insert(30); - avlTree.insert(20); - avlTree.insert(10); - avlTree.insert(25); - avlTree.insert(5); - avlTree.insert(15); - - avlTree.delete(20); // Test deletion - assertFalse(avlTree.search(20)); - assertTrue(avlTree.search(30)); - assertTrue(avlTree.search(25)); + void testSearchNonExistentElement() { + avl.insert(50); + avl.insert(30); + avl.insert(70); + + assertFalse(avl.search(100), "Should not find non-existent element"); + assertFalse(avl.search(25), "Should not find non-existent element"); + } + + @Test + void testSearchInEmptyTree() { + assertFalse(avl.search(10), "Should not find element in empty tree"); + } + + @Test + void testInorderTraversal() { + avl.insert(50); + avl.insert(30); + avl.insert(70); + avl.insert(20); + avl.insert(40); + avl.insert(60); + avl.insert(80); + + String expected = "20 30 40 50 60 70 80"; + assertEquals(expected, avl.inorder(), "Inorder traversal should be sorted"); + } + + @Test + void testPreorderTraversal() { + avl.insert(50); + avl.insert(30); + avl.insert(70); + + String result = avl.preorder(); + assertTrue(result.contains("50") && result.contains("30") && result.contains("70"), + "Preorder traversal should contain all elements"); + } + + @Test + void testDeleteLeafNode() { + avl.insert(50); + avl.insert(30); + avl.insert(70); + avl.insert(20); + + avl.delete(20); + assertFalse(avl.search(20), "Deleted leaf node should not be found"); + assertTrue(avl.isBalanced(), "Tree should remain balanced after deletion"); + assertEquals("30 50 70", avl.inorder(), "Tree should be correct after deletion"); + } + + @Test + void testDeleteNodeWithOneChild() { + avl.insert(50); + avl.insert(30); + avl.insert(70); + avl.insert(60); + + avl.delete(70); + assertFalse(avl.search(70), "Deleted node should not be found"); + assertTrue(avl.search(60), "Child of deleted node should still exist"); + assertTrue(avl.isBalanced(), "Tree should remain balanced after deletion"); + } + + @Test + void testDeleteNodeWithTwoChildren() { + avl.insert(50); + avl.insert(30); + avl.insert(70); + avl.insert(20); + avl.insert(40); + avl.insert(60); + avl.insert(80); + + avl.delete(50); + assertFalse(avl.search(50), "Deleted node should not be found"); + assertTrue(avl.isBalanced(), "Tree should remain balanced after deletion"); + String result = avl.inorder(); + assertEquals("20 30 40 60 70 80", result, "Tree should be correct after deletion"); + } + + @Test + void testDeleteRootNode() { + avl.insert(50); + avl.delete(50); + assertTrue(avl.isEmpty(), "Tree should be empty after deleting only node"); + } + + @Test + void testDeleteNonExistentElement() { + avl.insert(50); + avl.insert(30); + avl.insert(70); + + avl.delete(100); + assertEquals("30 50 70", avl.inorder(), "Tree should remain unchanged"); + assertTrue(avl.isBalanced(), "Tree should remain balanced"); + } + + @Test + void testDeleteWithRebalancing() { + // Create a tree that requires rebalancing after deletion + avl.insert(50); + avl.insert(25); + avl.insert(75); + avl.insert(10); + avl.insert(30); + avl.insert(60); + avl.insert(80); + avl.insert(5); + avl.insert(15); + + // Delete node that will trigger rebalancing + avl.delete(80); + avl.delete(75); + + assertTrue(avl.isBalanced(), "Tree should be balanced after deletions"); + assertFalse(avl.search(80), "Deleted element should not be found"); + assertFalse(avl.search(75), "Deleted element should not be found"); + } + + @Test + void testHeight() { + avl.insert(50); + assertEquals(1, avl.getHeight(), "Height should be 1"); + + avl.insert(30); + avl.insert(70); + assertEquals(2, avl.getHeight(), "Height should be 2"); + + avl.insert(20); + avl.insert(40); + avl.insert(60); + avl.insert(80); + // AVL tree should keep height minimal + assertTrue(avl.getHeight() <= 4, "Height should be logarithmic"); + assertTrue(avl.isBalanced(), "Tree should be balanced"); + } + + @Test + void testInsertDuplicates() { + avl.insert(50); + avl.insert(30); + avl.insert(50); // Duplicate + + String result = avl.inorder(); + assertTrue(result.contains("30") && result.contains("50"), + "Tree should contain all unique values"); + assertTrue(avl.isBalanced(), "Tree should remain balanced"); + } + + @Test + void testComplexOperations() { + // Insert elements + avl.insert(50); + avl.insert(30); + avl.insert(70); + avl.insert(20); + avl.insert(40); + avl.insert(60); + avl.insert(80); + + // Verify structure + assertEquals("20 30 40 50 60 70 80", avl.inorder()); + assertTrue(avl.isBalanced(), "Tree should be balanced"); + + // Delete some elements + avl.delete(20); + avl.delete(70); + + // Verify after deletions + assertEquals("30 40 50 60 80", avl.inorder()); + assertTrue(avl.isBalanced(), "Tree should remain balanced after deletions"); + + // Search for remaining elements + assertTrue(avl.search(50)); + assertTrue(avl.search(30)); + assertFalse(avl.search(20)); + assertFalse(avl.search(70)); + } + + @Test + void testAscendingOrder() { + // Insert in ascending order - AVL should handle this efficiently + for (int i = 1; i <= 10; i++) { + avl.insert(i); + } + + assertEquals("1 2 3 4 5 6 7 8 9 10", avl.inorder()); + assertTrue(avl.isBalanced(), "Tree should remain balanced with sequential inserts"); + // Height should be logarithmic, not linear + assertTrue(avl.getHeight() <= 5, "Height should be logarithmic for 10 elements"); + } + + @Test + void testDescendingOrder() { + // Insert in descending order - AVL should handle this efficiently + for (int i = 10; i >= 1; i--) { + avl.insert(i); + } + + assertEquals("1 2 3 4 5 6 7 8 9 10", avl.inorder()); + assertTrue(avl.isBalanced(), "Tree should remain balanced with reverse sequential inserts"); + assertTrue(avl.getHeight() <= 5, "Height should be logarithmic for 10 elements"); + } + + @Test + void testLargeNumberOfElements() { + // Insert many elements to test balancing efficiency + for (int i = 1; i <= 100; i++) { + avl.insert(i); + assertTrue(avl.isBalanced(), "Tree should remain balanced at every insertion"); + } + + // AVL tree with 100 elements should have height around log2(100) ≈ 7 + assertTrue(avl.getHeight() <= 10, "Height should be logarithmic for 100 elements"); + + // Verify all elements are searchable + for (int i = 1; i <= 100; i++) { + assertTrue(avl.search(i), "Should find element " + i); + } + } + + @Test + void testBalanceAfterMultipleDeletions() { + // Insert elements + for (int i = 1; i <= 15; i++) { + avl.insert(i); + } + + // Delete half of them + for (int i = 1; i <= 15; i += 2) { + avl.delete(i); + } + + assertTrue(avl.isBalanced(), "Tree should remain balanced after multiple deletions"); + + // Verify remaining elements + for (int i = 2; i <= 14; i += 2) { + assertTrue(avl.search(i), "Should find remaining element " + i); + } + } + + @Test + void testEmptyTreeAfterDeletions() { + avl.insert(10); + avl.insert(20); + avl.insert(30); + + avl.delete(10); + avl.delete(20); + avl.delete(30); + + assertTrue(avl.isEmpty(), "Tree should be empty after all deletions"); + assertEquals(0, avl.getHeight(), "Height should be 0 for empty tree"); } } diff --git a/src/test/java/com/thealgorithms/datastructures/trees/BinarySearchTreeTest.java b/src/test/java/com/thealgorithms/datastructures/trees/BinarySearchTreeTest.java new file mode 100644 index 000000000000..ab5d1ca1f7ce --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/BinarySearchTreeTest.java @@ -0,0 +1,242 @@ +package com.thealgorithms.datastructures.trees; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Test class for BinarySearchTree + * + * @author Raghu0703 + */ +class BinarySearchTreeTest { + + private BinarySearchTree bst; + + @BeforeEach + void setUp() { + bst = new BinarySearchTree(); + } + + @Test + void testEmptyTree() { + assertTrue(bst.isEmpty(), "New tree should be empty"); + assertEquals(0, bst.height(), "Empty tree height should be 0"); + } + + @Test + void testInsertSingleElement() { + bst.insert(10); + assertFalse(bst.isEmpty(), "Tree should not be empty after insert"); + assertTrue(bst.search(10), "Should find inserted element"); + assertEquals(1, bst.height(), "Height should be 1 with single element"); + } + + @Test + void testInsertMultipleElements() { + bst.insert(50); + bst.insert(30); + bst.insert(70); + bst.insert(20); + bst.insert(40); + bst.insert(60); + bst.insert(80); + + assertTrue(bst.search(50), "Should find 50"); + assertTrue(bst.search(30), "Should find 30"); + assertTrue(bst.search(70), "Should find 70"); + assertTrue(bst.search(20), "Should find 20"); + assertTrue(bst.search(40), "Should find 40"); + assertTrue(bst.search(60), "Should find 60"); + assertTrue(bst.search(80), "Should find 80"); + } + + @Test + void testSearchNonExistentElement() { + bst.insert(50); + bst.insert(30); + bst.insert(70); + + assertFalse(bst.search(100), "Should not find non-existent element"); + assertFalse(bst.search(25), "Should not find non-existent element"); + } + + @Test + void testSearchInEmptyTree() { + assertFalse(bst.search(10), "Should not find element in empty tree"); + } + + @Test + void testInorderTraversal() { + bst.insert(50); + bst.insert(30); + bst.insert(70); + bst.insert(20); + bst.insert(40); + bst.insert(60); + bst.insert(80); + + String expected = "20 30 40 50 60 70 80"; + assertEquals(expected, bst.inorder(), "Inorder traversal should be sorted"); + } + + @Test + void testPreorderTraversal() { + bst.insert(50); + bst.insert(30); + bst.insert(70); + bst.insert(20); + bst.insert(40); + + String expected = "50 30 20 40 70"; + assertEquals(expected, bst.preorder(), "Preorder traversal should match"); + } + + @Test + void testPostorderTraversal() { + bst.insert(50); + bst.insert(30); + bst.insert(70); + bst.insert(20); + bst.insert(40); + + String expected = "20 40 30 70 50"; + assertEquals(expected, bst.postorder(), "Postorder traversal should match"); + } + + @Test + void testDeleteLeafNode() { + bst.insert(50); + bst.insert(30); + bst.insert(70); + bst.insert(20); + + bst.delete(20); + assertFalse(bst.search(20), "Deleted leaf node should not be found"); + assertEquals("30 50 70", bst.inorder(), "Tree should be correct after deletion"); + } + + @Test + void testDeleteNodeWithOneChild() { + bst.insert(50); + bst.insert(30); + bst.insert(70); + bst.insert(60); + + bst.delete(70); + assertFalse(bst.search(70), "Deleted node should not be found"); + assertTrue(bst.search(60), "Child of deleted node should still exist"); + assertEquals("30 50 60", bst.inorder(), "Tree should be correct after deletion"); + } + + @Test + void testDeleteNodeWithTwoChildren() { + bst.insert(50); + bst.insert(30); + bst.insert(70); + bst.insert(20); + bst.insert(40); + bst.insert(60); + bst.insert(80); + + bst.delete(50); + assertFalse(bst.search(50), "Deleted node should not be found"); + assertEquals("20 30 40 60 70 80", bst.inorder(), "Tree should be correct after deletion"); + } + + @Test + void testDeleteRootNode() { + bst.insert(50); + bst.delete(50); + assertTrue(bst.isEmpty(), "Tree should be empty after deleting only node"); + } + + @Test + void testDeleteNonExistentElement() { + bst.insert(50); + bst.insert(30); + bst.insert(70); + + bst.delete(100); + assertEquals("30 50 70", bst.inorder(), "Tree should remain unchanged"); + } + + @Test + void testHeight() { + bst.insert(50); + assertEquals(1, bst.height(), "Height should be 1"); + + bst.insert(30); + bst.insert(70); + assertEquals(2, bst.height(), "Height should be 2"); + + bst.insert(20); + assertEquals(3, bst.height(), "Height should be 3"); + } + + @Test + void testInsertDuplicates() { + bst.insert(50); + bst.insert(30); + bst.insert(50); // Duplicate + + String result = bst.inorder(); + // BST should handle duplicates by either ignoring or placing right + assertTrue(result.contains("30") && result.contains("50"), + "Tree should contain all unique values"); + } + + @Test + void testComplexOperations() { + // Insert elements + bst.insert(50); + bst.insert(30); + bst.insert(70); + bst.insert(20); + bst.insert(40); + bst.insert(60); + bst.insert(80); + + // Verify structure + assertEquals("20 30 40 50 60 70 80", bst.inorder()); + + // Delete some elements + bst.delete(20); + bst.delete(70); + + // Verify after deletions + assertEquals("30 40 50 60 80", bst.inorder()); + + // Search for remaining elements + assertTrue(bst.search(50)); + assertTrue(bst.search(30)); + assertFalse(bst.search(20)); + assertFalse(bst.search(70)); + } + + @Test + void testAscendingOrder() { + // Insert in ascending order + bst.insert(10); + bst.insert(20); + bst.insert(30); + bst.insert(40); + bst.insert(50); + + assertEquals("10 20 30 40 50", bst.inorder()); + assertTrue(bst.search(30)); + } + + @Test + void testDescendingOrder() { + // Insert in descending order + bst.insert(50); + bst.insert(40); + bst.insert(30); + bst.insert(20); + bst.insert(10); + + assertEquals("10 20 30 40 50", bst.inorder()); + assertTrue(bst.search(30)); + } +} From 3ddb110eae6bbc006cc69a2f73d17c16c3b2e3b5 Mon Sep 17 00:00:00 2001 From: Raghu0703 <2400030362@kluniversity.in> Date: Sun, 23 Nov 2025 23:47:30 +0530 Subject: [PATCH 2/5] fix: Make classes final and Node class static for checkstyle compliance --- .../java/com/thealgorithms/datastructures/trees/AVLTree.java | 4 ++-- .../thealgorithms/datastructures/trees/BinarySearchTree.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/thealgorithms/datastructures/trees/AVLTree.java b/src/main/java/com/thealgorithms/datastructures/trees/AVLTree.java index 553c63c847d5..29bd21b6014b 100644 --- a/src/main/java/com/thealgorithms/datastructures/trees/AVLTree.java +++ b/src/main/java/com/thealgorithms/datastructures/trees/AVLTree.java @@ -7,12 +7,12 @@ * * @author Raghu0703 */ -public class AVLTree { +public final class AVLTree { /** * Node class representing each element in the AVL Tree */ - class Node { + static class Node { int data; int height; Node left, right; diff --git a/src/main/java/com/thealgorithms/datastructures/trees/BinarySearchTree.java b/src/main/java/com/thealgorithms/datastructures/trees/BinarySearchTree.java index ee6f1b479235..0093f5dcce0d 100644 --- a/src/main/java/com/thealgorithms/datastructures/trees/BinarySearchTree.java +++ b/src/main/java/com/thealgorithms/datastructures/trees/BinarySearchTree.java @@ -5,12 +5,12 @@ * * @author Raghu0703 */ -public class BinarySearchTree { +public final class BinarySearchTree { /** * Node class representing each element in the BST */ - class Node { + static class Node { int data; Node left, right; From 86e54f5adc02c0433a2ad16c1e125ce8da10bf29 Mon Sep 17 00:00:00 2001 From: Raghu0703 <2400030362@kluniversity.in> Date: Sun, 23 Nov 2025 23:57:14 +0530 Subject: [PATCH 3/5] fix: Apply clang-format to BST and AVL Tree files --- .../datastructures/trees/AVLTree.java | 146 +++++++++--------- .../trees/BinarySearchTree.java | 84 +++++----- 2 files changed, 115 insertions(+), 115 deletions(-) diff --git a/src/main/java/com/thealgorithms/datastructures/trees/AVLTree.java b/src/main/java/com/thealgorithms/datastructures/trees/AVLTree.java index 29bd21b6014b..61ff9e1731ea 100644 --- a/src/main/java/com/thealgorithms/datastructures/trees/AVLTree.java +++ b/src/main/java/com/thealgorithms/datastructures/trees/AVLTree.java @@ -4,19 +4,19 @@ * AVL Tree (Adelson-Velsky and Landis Tree) implementation. * A self-balancing Binary Search Tree where the difference between heights * of left and right subtrees cannot be more than one for all nodes. - * + * * @author Raghu0703 */ public final class AVLTree { - + /** * Node class representing each element in the AVL Tree */ - static class Node { + static class Node { int data; int height; Node left, right; - + public Node(int data) { this.data = data; this.height = 1; @@ -24,19 +24,19 @@ public Node(int data) { this.right = null; } } - + private Node root; - + /** * Constructor to initialize empty AVL Tree */ public AVLTree() { this.root = null; } - + /** * Get the height of a node - * + * * @param node the node * @return height of the node, 0 if null */ @@ -46,10 +46,10 @@ private int height(Node node) { } return node.height; } - + /** * Get the balance factor of a node - * + * * @param node the node * @return balance factor (left height - right height) */ @@ -59,61 +59,61 @@ private int getBalance(Node node) { } return height(node.left) - height(node.right); } - + /** * Right rotate subtree rooted with y - * + * * @param y the root of subtree to rotate * @return new root after rotation */ private Node rightRotate(Node y) { Node x = y.left; Node T2 = x.right; - + // Perform rotation x.right = y; y.left = T2; - + // Update heights y.height = Math.max(height(y.left), height(y.right)) + 1; x.height = Math.max(height(x.left), height(x.right)) + 1; - + return x; } - + /** * Left rotate subtree rooted with x - * + * * @param x the root of subtree to rotate * @return new root after rotation */ private Node leftRotate(Node x) { Node y = x.right; Node T2 = y.left; - + // Perform rotation y.left = x; x.right = T2; - + // Update heights x.height = Math.max(height(x.left), height(x.right)) + 1; y.height = Math.max(height(y.left), height(y.right)) + 1; - + return y; } - + /** * Insert a value into the AVL Tree - * + * * @param data the value to insert */ public void insert(int data) { root = insertRec(root, data); } - + /** * Recursive helper method to insert a value and balance the tree - * + * * @param node current node * @param data value to insert * @return the balanced node @@ -123,7 +123,7 @@ private Node insertRec(Node node, int data) { if (node == null) { return new Node(data); } - + if (data < node.data) { node.left = insertRec(node.left, data); } else if (data > node.data) { @@ -132,43 +132,43 @@ private Node insertRec(Node node, int data) { // Duplicate values not allowed return node; } - + // Update height of current node node.height = 1 + Math.max(height(node.left), height(node.right)); - + // Get balance factor int balance = getBalance(node); - + // If node is unbalanced, there are 4 cases - + // Left Left Case if (balance > 1 && data < node.left.data) { return rightRotate(node); } - + // Right Right Case if (balance < -1 && data > node.right.data) { return leftRotate(node); } - + // Left Right Case if (balance > 1 && data > node.left.data) { node.left = leftRotate(node.left); return rightRotate(node); } - + // Right Left Case if (balance < -1 && data < node.right.data) { node.right = rightRotate(node.right); return leftRotate(node); } - + return node; } - + /** * Find the node with minimum value - * + * * @param node the root of subtree * @return node with minimum value */ @@ -179,19 +179,19 @@ private Node minValueNode(Node node) { } return current; } - + /** * Delete a value from the AVL Tree - * + * * @param data the value to delete */ public void delete(int data) { root = deleteRec(root, data); } - + /** * Recursive helper method to delete a value and balance the tree - * + * * @param root current node * @param data value to delete * @return the balanced node @@ -201,7 +201,7 @@ private Node deleteRec(Node root, int data) { if (root == null) { return root; } - + if (data < root.data) { root.left = deleteRec(root.left, data); } else if (data > root.data) { @@ -210,7 +210,7 @@ private Node deleteRec(Node root, int data) { // Node with only one child or no child if ((root.left == null) || (root.right == null)) { Node temp = root.left != null ? root.left : root.right; - + // No child case if (temp == null) { temp = root; @@ -226,58 +226,58 @@ private Node deleteRec(Node root, int data) { root.right = deleteRec(root.right, temp.data); } } - + // If tree had only one node if (root == null) { return root; } - + // Update height root.height = Math.max(height(root.left), height(root.right)) + 1; - + // Get balance factor int balance = getBalance(root); - + // If unbalanced, there are 4 cases - + // Left Left Case if (balance > 1 && getBalance(root.left) >= 0) { return rightRotate(root); } - + // Left Right Case if (balance > 1 && getBalance(root.left) < 0) { root.left = leftRotate(root.left); return rightRotate(root); } - + // Right Right Case if (balance < -1 && getBalance(root.right) <= 0) { return leftRotate(root); } - + // Right Left Case if (balance < -1 && getBalance(root.right) > 0) { root.right = rightRotate(root.right); return leftRotate(root); } - + return root; } - + /** * Search for a value in the AVL Tree - * + * * @param data the value to search for * @return true if found, false otherwise */ public boolean search(int data) { return searchRec(root, data); } - + /** * Recursive helper method to search for a value - * + * * @param root current node * @param data value to search for * @return true if found, false otherwise @@ -286,21 +286,21 @@ private boolean searchRec(Node root, int data) { if (root == null) { return false; } - + if (root.data == data) { return true; } - + if (data < root.data) { return searchRec(root.left, data); } - + return searchRec(root.right, data); } - + /** * Inorder traversal (Left-Root-Right) - * + * * @return string representation of inorder traversal */ public String inorder() { @@ -308,7 +308,7 @@ public String inorder() { inorderRec(root, result); return result.toString().trim(); } - + /** * Recursive helper for inorder traversal */ @@ -319,10 +319,10 @@ private void inorderRec(Node root, StringBuilder result) { inorderRec(root.right, result); } } - + /** * Preorder traversal (Root-Left-Right) - * + * * @return string representation of preorder traversal */ public String preorder() { @@ -330,7 +330,7 @@ public String preorder() { preorderRec(root, result); return result.toString().trim(); } - + /** * Recursive helper for preorder traversal */ @@ -341,34 +341,34 @@ private void preorderRec(Node root, StringBuilder result) { preorderRec(root.right, result); } } - + /** * Get the height of the tree - * + * * @return the height of the tree */ public int getHeight() { return height(root); } - + /** * Check if the tree is empty - * + * * @return true if empty, false otherwise */ public boolean isEmpty() { return root == null; } - + /** * Check if the tree is balanced (for testing purposes) - * + * * @return true if balanced, false otherwise */ public boolean isBalanced() { return isBalancedRec(root); } - + /** * Recursive helper to check if tree is balanced */ @@ -376,13 +376,13 @@ private boolean isBalancedRec(Node node) { if (node == null) { return true; } - + int balance = getBalance(node); - + if (Math.abs(balance) > 1) { return false; } - + return isBalancedRec(node.left) && isBalancedRec(node.right); } } diff --git a/src/main/java/com/thealgorithms/datastructures/trees/BinarySearchTree.java b/src/main/java/com/thealgorithms/datastructures/trees/BinarySearchTree.java index 0093f5dcce0d..8c85d0dfeb63 100644 --- a/src/main/java/com/thealgorithms/datastructures/trees/BinarySearchTree.java +++ b/src/main/java/com/thealgorithms/datastructures/trees/BinarySearchTree.java @@ -2,46 +2,46 @@ /** * Binary Search Tree implementation with insert, search, delete, and traversal operations. - * + * * @author Raghu0703 */ public final class BinarySearchTree { - + /** * Node class representing each element in the BST */ - static class Node { + static class Node { int data; Node left, right; - + public Node(int data) { this.data = data; this.left = null; this.right = null; } } - + private Node root; - + /** * Constructor to initialize empty BST */ public BinarySearchTree() { this.root = null; } - + /** * Insert a value into the BST - * + * * @param data the value to insert */ public void insert(int data) { root = insertRec(root, data); } - + /** * Recursive helper method to insert a value - * + * * @param root current node * @param data value to insert * @return the modified node @@ -51,29 +51,29 @@ private Node insertRec(Node root, int data) { root = new Node(data); return root; } - + if (data < root.data) { root.left = insertRec(root.left, data); } else if (data > root.data) { root.right = insertRec(root.right, data); } - + return root; } - + /** * Search for a value in the BST - * + * * @param data the value to search for * @return true if found, false otherwise */ public boolean search(int data) { return searchRec(root, data); } - + /** * Recursive helper method to search for a value - * + * * @param root current node * @param data value to search for * @return true if found, false otherwise @@ -82,30 +82,30 @@ private boolean searchRec(Node root, int data) { if (root == null) { return false; } - + if (root.data == data) { return true; } - + if (data < root.data) { return searchRec(root.left, data); } - + return searchRec(root.right, data); } - + /** * Delete a value from the BST - * + * * @param data the value to delete */ public void delete(int data) { root = deleteRec(root, data); } - + /** * Recursive helper method to delete a value - * + * * @param root current node * @param data value to delete * @return the modified node @@ -114,7 +114,7 @@ private Node deleteRec(Node root, int data) { if (root == null) { return root; } - + if (data < root.data) { root.left = deleteRec(root.left, data); } else if (data > root.data) { @@ -126,18 +126,18 @@ private Node deleteRec(Node root, int data) { } else if (root.right == null) { return root.left; } - + // Node with two children: Get inorder successor root.data = minValue(root.right); root.right = deleteRec(root.right, root.data); } - + return root; } - + /** * Find the minimum value in a subtree - * + * * @param root the root of the subtree * @return the minimum value */ @@ -149,10 +149,10 @@ private int minValue(Node root) { } return minValue; } - + /** * Inorder traversal (Left-Root-Right) - * + * * @return string representation of inorder traversal */ public String inorder() { @@ -160,7 +160,7 @@ public String inorder() { inorderRec(root, result); return result.toString().trim(); } - + /** * Recursive helper for inorder traversal */ @@ -171,10 +171,10 @@ private void inorderRec(Node root, StringBuilder result) { inorderRec(root.right, result); } } - + /** * Preorder traversal (Root-Left-Right) - * + * * @return string representation of preorder traversal */ public String preorder() { @@ -182,7 +182,7 @@ public String preorder() { preorderRec(root, result); return result.toString().trim(); } - + /** * Recursive helper for preorder traversal */ @@ -193,10 +193,10 @@ private void preorderRec(Node root, StringBuilder result) { preorderRec(root.right, result); } } - + /** * Postorder traversal (Left-Right-Root) - * + * * @return string representation of postorder traversal */ public String postorder() { @@ -204,7 +204,7 @@ public String postorder() { postorderRec(root, result); return result.toString().trim(); } - + /** * Recursive helper for postorder traversal */ @@ -215,16 +215,16 @@ private void postorderRec(Node root, StringBuilder result) { result.append(root.data).append(" "); } } - + /** * Get the height of the tree - * + * * @return the height of the tree */ public int height() { return heightRec(root); } - + /** * Recursive helper to calculate height */ @@ -234,10 +234,10 @@ private int heightRec(Node root) { } return 1 + Math.max(heightRec(root.left), heightRec(root.right)); } - + /** * Check if the tree is empty - * + * * @return true if empty, false otherwise */ public boolean isEmpty() { From a1c276c1e18cb6ce4d72cb090bd513899872032e Mon Sep 17 00:00:00 2001 From: Raghu0703 <2400030362@kluniversity.in> Date: Mon, 24 Nov 2025 00:14:04 +0530 Subject: [PATCH 4/5] fix: Apply clang-format to all files and remove trailing whitespace --- .../datastructures/trees/AVLTreeTest.java | 123 +++++++++--------- .../trees/BinarySearchTreeTest.java | 82 ++++++------ 2 files changed, 102 insertions(+), 103 deletions(-) diff --git a/src/test/java/com/thealgorithms/datastructures/trees/AVLTreeTest.java b/src/test/java/com/thealgorithms/datastructures/trees/AVLTreeTest.java index 6bd14e25f2d1..97079ace2deb 100644 --- a/src/test/java/com/thealgorithms/datastructures/trees/AVLTreeTest.java +++ b/src/test/java/com/thealgorithms/datastructures/trees/AVLTreeTest.java @@ -1,30 +1,31 @@ package com.thealgorithms.datastructures.trees; import static org.junit.jupiter.api.Assertions.*; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Test class for AVLTree - * + * * @author Raghu0703 */ class AVLTreeTest { - + private AVLTree avl; - + @BeforeEach void setUp() { avl = new AVLTree(); } - + @Test void testEmptyTree() { assertTrue(avl.isEmpty(), "New tree should be empty"); assertEquals(0, avl.getHeight(), "Empty tree height should be 0"); assertTrue(avl.isBalanced(), "Empty tree should be balanced"); } - + @Test void testInsertSingleElement() { avl.insert(10); @@ -33,7 +34,7 @@ void testInsertSingleElement() { assertEquals(1, avl.getHeight(), "Height should be 1 with single element"); assertTrue(avl.isBalanced(), "Tree should be balanced"); } - + @Test void testInsertMultipleElements() { avl.insert(50); @@ -43,7 +44,7 @@ void testInsertMultipleElements() { avl.insert(40); avl.insert(60); avl.insert(80); - + assertTrue(avl.search(50), "Should find 50"); assertTrue(avl.search(30), "Should find 30"); assertTrue(avl.search(70), "Should find 70"); @@ -53,70 +54,70 @@ void testInsertMultipleElements() { assertTrue(avl.search(80), "Should find 80"); assertTrue(avl.isBalanced(), "Tree should remain balanced"); } - + @Test void testLeftLeftRotation() { // Insert in descending order to trigger LL rotation avl.insert(30); avl.insert(20); avl.insert(10); - + assertEquals("10 20 30", avl.inorder(), "Tree should be balanced after LL rotation"); assertTrue(avl.isBalanced(), "Tree should be balanced"); assertEquals(2, avl.getHeight(), "Height should be 2 after balancing"); } - + @Test void testRightRightRotation() { // Insert in ascending order to trigger RR rotation avl.insert(10); avl.insert(20); avl.insert(30); - + assertEquals("10 20 30", avl.inorder(), "Tree should be balanced after RR rotation"); assertTrue(avl.isBalanced(), "Tree should be balanced"); assertEquals(2, avl.getHeight(), "Height should be 2 after balancing"); } - + @Test void testLeftRightRotation() { // Insert to trigger LR rotation avl.insert(30); avl.insert(10); avl.insert(20); - + assertEquals("10 20 30", avl.inorder(), "Tree should be balanced after LR rotation"); assertTrue(avl.isBalanced(), "Tree should be balanced"); assertEquals(2, avl.getHeight(), "Height should be 2 after balancing"); } - + @Test void testRightLeftRotation() { // Insert to trigger RL rotation avl.insert(10); avl.insert(30); avl.insert(20); - + assertEquals("10 20 30", avl.inorder(), "Tree should be balanced after RL rotation"); assertTrue(avl.isBalanced(), "Tree should be balanced"); assertEquals(2, avl.getHeight(), "Height should be 2 after balancing"); } - + @Test void testSearchNonExistentElement() { avl.insert(50); avl.insert(30); avl.insert(70); - + assertFalse(avl.search(100), "Should not find non-existent element"); assertFalse(avl.search(25), "Should not find non-existent element"); } - + @Test void testSearchInEmptyTree() { assertFalse(avl.search(10), "Should not find element in empty tree"); } - + @Test void testInorderTraversal() { avl.insert(50); @@ -126,48 +127,47 @@ void testInorderTraversal() { avl.insert(40); avl.insert(60); avl.insert(80); - + String expected = "20 30 40 50 60 70 80"; assertEquals(expected, avl.inorder(), "Inorder traversal should be sorted"); } - + @Test void testPreorderTraversal() { avl.insert(50); avl.insert(30); avl.insert(70); - + String result = avl.preorder(); - assertTrue(result.contains("50") && result.contains("30") && result.contains("70"), - "Preorder traversal should contain all elements"); + assertTrue(result.contains("50") && result.contains("30") && result.contains("70"), "Preorder traversal should contain all elements"); } - + @Test void testDeleteLeafNode() { avl.insert(50); avl.insert(30); avl.insert(70); avl.insert(20); - + avl.delete(20); assertFalse(avl.search(20), "Deleted leaf node should not be found"); assertTrue(avl.isBalanced(), "Tree should remain balanced after deletion"); assertEquals("30 50 70", avl.inorder(), "Tree should be correct after deletion"); } - + @Test void testDeleteNodeWithOneChild() { avl.insert(50); avl.insert(30); avl.insert(70); avl.insert(60); - + avl.delete(70); assertFalse(avl.search(70), "Deleted node should not be found"); assertTrue(avl.search(60), "Child of deleted node should still exist"); assertTrue(avl.isBalanced(), "Tree should remain balanced after deletion"); } - + @Test void testDeleteNodeWithTwoChildren() { avl.insert(50); @@ -177,32 +177,32 @@ void testDeleteNodeWithTwoChildren() { avl.insert(40); avl.insert(60); avl.insert(80); - + avl.delete(50); assertFalse(avl.search(50), "Deleted node should not be found"); assertTrue(avl.isBalanced(), "Tree should remain balanced after deletion"); String result = avl.inorder(); assertEquals("20 30 40 60 70 80", result, "Tree should be correct after deletion"); } - + @Test void testDeleteRootNode() { avl.insert(50); avl.delete(50); assertTrue(avl.isEmpty(), "Tree should be empty after deleting only node"); } - + @Test void testDeleteNonExistentElement() { avl.insert(50); avl.insert(30); avl.insert(70); - + avl.delete(100); assertEquals("30 50 70", avl.inorder(), "Tree should remain unchanged"); assertTrue(avl.isBalanced(), "Tree should remain balanced"); } - + @Test void testDeleteWithRebalancing() { // Create a tree that requires rebalancing after deletion @@ -215,25 +215,25 @@ void testDeleteWithRebalancing() { avl.insert(80); avl.insert(5); avl.insert(15); - + // Delete node that will trigger rebalancing avl.delete(80); avl.delete(75); - + assertTrue(avl.isBalanced(), "Tree should be balanced after deletions"); assertFalse(avl.search(80), "Deleted element should not be found"); assertFalse(avl.search(75), "Deleted element should not be found"); } - + @Test void testHeight() { avl.insert(50); assertEquals(1, avl.getHeight(), "Height should be 1"); - + avl.insert(30); avl.insert(70); assertEquals(2, avl.getHeight(), "Height should be 2"); - + avl.insert(20); avl.insert(40); avl.insert(60); @@ -242,19 +242,18 @@ void testHeight() { assertTrue(avl.getHeight() <= 4, "Height should be logarithmic"); assertTrue(avl.isBalanced(), "Tree should be balanced"); } - + @Test void testInsertDuplicates() { avl.insert(50); avl.insert(30); avl.insert(50); // Duplicate - + String result = avl.inorder(); - assertTrue(result.contains("30") && result.contains("50"), - "Tree should contain all unique values"); + assertTrue(result.contains("30") && result.contains("50"), "Tree should contain all unique values"); assertTrue(avl.isBalanced(), "Tree should remain balanced"); } - + @Test void testComplexOperations() { // Insert elements @@ -265,51 +264,51 @@ void testComplexOperations() { avl.insert(40); avl.insert(60); avl.insert(80); - + // Verify structure assertEquals("20 30 40 50 60 70 80", avl.inorder()); assertTrue(avl.isBalanced(), "Tree should be balanced"); - + // Delete some elements avl.delete(20); avl.delete(70); - + // Verify after deletions assertEquals("30 40 50 60 80", avl.inorder()); assertTrue(avl.isBalanced(), "Tree should remain balanced after deletions"); - + // Search for remaining elements assertTrue(avl.search(50)); assertTrue(avl.search(30)); assertFalse(avl.search(20)); assertFalse(avl.search(70)); } - + @Test void testAscendingOrder() { // Insert in ascending order - AVL should handle this efficiently for (int i = 1; i <= 10; i++) { avl.insert(i); } - + assertEquals("1 2 3 4 5 6 7 8 9 10", avl.inorder()); assertTrue(avl.isBalanced(), "Tree should remain balanced with sequential inserts"); // Height should be logarithmic, not linear assertTrue(avl.getHeight() <= 5, "Height should be logarithmic for 10 elements"); } - + @Test void testDescendingOrder() { // Insert in descending order - AVL should handle this efficiently for (int i = 10; i >= 1; i--) { avl.insert(i); } - + assertEquals("1 2 3 4 5 6 7 8 9 10", avl.inorder()); assertTrue(avl.isBalanced(), "Tree should remain balanced with reverse sequential inserts"); assertTrue(avl.getHeight() <= 5, "Height should be logarithmic for 10 elements"); } - + @Test void testLargeNumberOfElements() { // Insert many elements to test balancing efficiency @@ -317,46 +316,46 @@ void testLargeNumberOfElements() { avl.insert(i); assertTrue(avl.isBalanced(), "Tree should remain balanced at every insertion"); } - + // AVL tree with 100 elements should have height around log2(100) ≈ 7 assertTrue(avl.getHeight() <= 10, "Height should be logarithmic for 100 elements"); - + // Verify all elements are searchable for (int i = 1; i <= 100; i++) { assertTrue(avl.search(i), "Should find element " + i); } } - + @Test void testBalanceAfterMultipleDeletions() { // Insert elements for (int i = 1; i <= 15; i++) { avl.insert(i); } - + // Delete half of them for (int i = 1; i <= 15; i += 2) { avl.delete(i); } - + assertTrue(avl.isBalanced(), "Tree should remain balanced after multiple deletions"); - + // Verify remaining elements for (int i = 2; i <= 14; i += 2) { assertTrue(avl.search(i), "Should find remaining element " + i); } } - + @Test void testEmptyTreeAfterDeletions() { avl.insert(10); avl.insert(20); avl.insert(30); - + avl.delete(10); avl.delete(20); avl.delete(30); - + assertTrue(avl.isEmpty(), "Tree should be empty after all deletions"); assertEquals(0, avl.getHeight(), "Height should be 0 for empty tree"); } diff --git a/src/test/java/com/thealgorithms/datastructures/trees/BinarySearchTreeTest.java b/src/test/java/com/thealgorithms/datastructures/trees/BinarySearchTreeTest.java index ab5d1ca1f7ce..6d9ed9dff711 100644 --- a/src/test/java/com/thealgorithms/datastructures/trees/BinarySearchTreeTest.java +++ b/src/test/java/com/thealgorithms/datastructures/trees/BinarySearchTreeTest.java @@ -1,29 +1,30 @@ package com.thealgorithms.datastructures.trees; import static org.junit.jupiter.api.Assertions.*; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Test class for BinarySearchTree - * + * * @author Raghu0703 */ class BinarySearchTreeTest { - + private BinarySearchTree bst; - + @BeforeEach void setUp() { bst = new BinarySearchTree(); } - + @Test void testEmptyTree() { assertTrue(bst.isEmpty(), "New tree should be empty"); assertEquals(0, bst.height(), "Empty tree height should be 0"); } - + @Test void testInsertSingleElement() { bst.insert(10); @@ -31,7 +32,7 @@ void testInsertSingleElement() { assertTrue(bst.search(10), "Should find inserted element"); assertEquals(1, bst.height(), "Height should be 1 with single element"); } - + @Test void testInsertMultipleElements() { bst.insert(50); @@ -41,7 +42,7 @@ void testInsertMultipleElements() { bst.insert(40); bst.insert(60); bst.insert(80); - + assertTrue(bst.search(50), "Should find 50"); assertTrue(bst.search(30), "Should find 30"); assertTrue(bst.search(70), "Should find 70"); @@ -50,22 +51,22 @@ void testInsertMultipleElements() { assertTrue(bst.search(60), "Should find 60"); assertTrue(bst.search(80), "Should find 80"); } - + @Test void testSearchNonExistentElement() { bst.insert(50); bst.insert(30); bst.insert(70); - + assertFalse(bst.search(100), "Should not find non-existent element"); assertFalse(bst.search(25), "Should not find non-existent element"); } - + @Test void testSearchInEmptyTree() { assertFalse(bst.search(10), "Should not find element in empty tree"); } - + @Test void testInorderTraversal() { bst.insert(50); @@ -75,11 +76,11 @@ void testInorderTraversal() { bst.insert(40); bst.insert(60); bst.insert(80); - + String expected = "20 30 40 50 60 70 80"; assertEquals(expected, bst.inorder(), "Inorder traversal should be sorted"); } - + @Test void testPreorderTraversal() { bst.insert(50); @@ -87,11 +88,11 @@ void testPreorderTraversal() { bst.insert(70); bst.insert(20); bst.insert(40); - + String expected = "50 30 20 40 70"; assertEquals(expected, bst.preorder(), "Preorder traversal should match"); } - + @Test void testPostorderTraversal() { bst.insert(50); @@ -99,36 +100,36 @@ void testPostorderTraversal() { bst.insert(70); bst.insert(20); bst.insert(40); - + String expected = "20 40 30 70 50"; assertEquals(expected, bst.postorder(), "Postorder traversal should match"); } - + @Test void testDeleteLeafNode() { bst.insert(50); bst.insert(30); bst.insert(70); bst.insert(20); - + bst.delete(20); assertFalse(bst.search(20), "Deleted leaf node should not be found"); assertEquals("30 50 70", bst.inorder(), "Tree should be correct after deletion"); } - + @Test void testDeleteNodeWithOneChild() { bst.insert(50); bst.insert(30); bst.insert(70); bst.insert(60); - + bst.delete(70); assertFalse(bst.search(70), "Deleted node should not be found"); assertTrue(bst.search(60), "Child of deleted node should still exist"); assertEquals("30 50 60", bst.inorder(), "Tree should be correct after deletion"); } - + @Test void testDeleteNodeWithTwoChildren() { bst.insert(50); @@ -138,54 +139,53 @@ void testDeleteNodeWithTwoChildren() { bst.insert(40); bst.insert(60); bst.insert(80); - + bst.delete(50); assertFalse(bst.search(50), "Deleted node should not be found"); assertEquals("20 30 40 60 70 80", bst.inorder(), "Tree should be correct after deletion"); } - + @Test void testDeleteRootNode() { bst.insert(50); bst.delete(50); assertTrue(bst.isEmpty(), "Tree should be empty after deleting only node"); } - + @Test void testDeleteNonExistentElement() { bst.insert(50); bst.insert(30); bst.insert(70); - + bst.delete(100); assertEquals("30 50 70", bst.inorder(), "Tree should remain unchanged"); } - + @Test void testHeight() { bst.insert(50); assertEquals(1, bst.height(), "Height should be 1"); - + bst.insert(30); bst.insert(70); assertEquals(2, bst.height(), "Height should be 2"); - + bst.insert(20); assertEquals(3, bst.height(), "Height should be 3"); } - + @Test void testInsertDuplicates() { bst.insert(50); bst.insert(30); bst.insert(50); // Duplicate - + String result = bst.inorder(); // BST should handle duplicates by either ignoring or placing right - assertTrue(result.contains("30") && result.contains("50"), - "Tree should contain all unique values"); + assertTrue(result.contains("30") && result.contains("50"), "Tree should contain all unique values"); } - + @Test void testComplexOperations() { // Insert elements @@ -196,24 +196,24 @@ void testComplexOperations() { bst.insert(40); bst.insert(60); bst.insert(80); - + // Verify structure assertEquals("20 30 40 50 60 70 80", bst.inorder()); - + // Delete some elements bst.delete(20); bst.delete(70); - + // Verify after deletions assertEquals("30 40 50 60 80", bst.inorder()); - + // Search for remaining elements assertTrue(bst.search(50)); assertTrue(bst.search(30)); assertFalse(bst.search(20)); assertFalse(bst.search(70)); } - + @Test void testAscendingOrder() { // Insert in ascending order @@ -222,11 +222,11 @@ void testAscendingOrder() { bst.insert(30); bst.insert(40); bst.insert(50); - + assertEquals("10 20 30 40 50", bst.inorder()); assertTrue(bst.search(30)); } - + @Test void testDescendingOrder() { // Insert in descending order @@ -235,7 +235,7 @@ void testDescendingOrder() { bst.insert(30); bst.insert(20); bst.insert(10); - + assertEquals("10 20 30 40 50", bst.inorder()); assertTrue(bst.search(30)); } From 3df51a9897e0d089de6f496563b41b76b6849cae Mon Sep 17 00:00:00 2001 From: Raghu0703 <2400030362@kluniversity.in> Date: Mon, 24 Nov 2025 00:30:25 +0530 Subject: [PATCH 5/5] fix: Resolve all 8 checkstyle violations --- .../datastructures/trees/AVLTree.java | 347 +++--------------- .../trees/BinarySearchTree.java | 234 +++--------- .../datastructures/trees/AVLTreeTest.java | 4 +- .../trees/BinarySearchTreeTest.java | 5 +- 4 files changed, 107 insertions(+), 483 deletions(-) diff --git a/src/main/java/com/thealgorithms/datastructures/trees/AVLTree.java b/src/main/java/com/thealgorithms/datastructures/trees/AVLTree.java index 61ff9e1731ea..d49da467c926 100644 --- a/src/main/java/com/thealgorithms/datastructures/trees/AVLTree.java +++ b/src/main/java/com/thealgorithms/datastructures/trees/AVLTree.java @@ -1,5 +1,4 @@ package com.thealgorithms.datastructures.trees; - /** * AVL Tree (Adelson-Velsky and Landis Tree) implementation. * A self-balancing Binary Search Tree where the difference between heights @@ -8,16 +7,13 @@ * @author Raghu0703 */ public final class AVLTree { - - /** - * Node class representing each element in the AVL Tree - */ static class Node { int data; int height; - Node left, right; + Node left; + Node right; - public Node(int data) { + Node(int data) { this.data = data; this.height = 1; this.left = null; @@ -27,362 +23,121 @@ public Node(int data) { private Node root; - /** - * Constructor to initialize empty AVL Tree - */ public AVLTree() { this.root = null; } - /** - * Get the height of a node - * - * @param node the node - * @return height of the node, 0 if null - */ private int height(Node node) { - if (node == null) { - return 0; - } - return node.height; + return node == null ? 0 : node.height; } - /** - * Get the balance factor of a node - * - * @param node the node - * @return balance factor (left height - right height) - */ private int getBalance(Node node) { - if (node == null) { - return 0; - } - return height(node.left) - height(node.right); + return node == null ? 0 : height(node.left) - height(node.right); + } + + private void updateHeight(Node node) { + node.height = Math.max(height(node.left), height(node.right)) + 1; } - /** - * Right rotate subtree rooted with y - * - * @param y the root of subtree to rotate - * @return new root after rotation - */ private Node rightRotate(Node y) { Node x = y.left; - Node T2 = x.right; - - // Perform rotation + Node t2 = x.right; x.right = y; - y.left = T2; - - // Update heights - y.height = Math.max(height(y.left), height(y.right)) + 1; - x.height = Math.max(height(x.left), height(x.right)) + 1; - + y.left = t2; + updateHeight(y); + updateHeight(x); return x; } - /** - * Left rotate subtree rooted with x - * - * @param x the root of subtree to rotate - * @return new root after rotation - */ private Node leftRotate(Node x) { Node y = x.right; - Node T2 = y.left; - - // Perform rotation + Node t2 = y.left; y.left = x; - x.right = T2; - - // Update heights - x.height = Math.max(height(x.left), height(x.right)) + 1; - y.height = Math.max(height(y.left), height(y.right)) + 1; - + x.right = t2; + updateHeight(x); + updateHeight(y); return y; } - /** - * Insert a value into the AVL Tree - * - * @param data the value to insert - */ - public void insert(int data) { - root = insertRec(root, data); + public void insert(int value) { + root = insertRec(root, value); } - /** - * Recursive helper method to insert a value and balance the tree - * - * @param node current node - * @param data value to insert - * @return the balanced node - */ - private Node insertRec(Node node, int data) { - // Perform normal BST insertion + private Node insertRec(Node node, int value) { if (node == null) { - return new Node(data); + return new Node(value); } - - if (data < node.data) { - node.left = insertRec(node.left, data); - } else if (data > node.data) { - node.right = insertRec(node.right, data); + if (value < node.data) { + node.left = insertRec(node.left, value); + } else if (value > node.data) { + node.right = insertRec(node.right, value); } else { - // Duplicate values not allowed return node; } - - // Update height of current node - node.height = 1 + Math.max(height(node.left), height(node.right)); - - // Get balance factor + updateHeight(node); int balance = getBalance(node); - - // If node is unbalanced, there are 4 cases - - // Left Left Case - if (balance > 1 && data < node.left.data) { + if (balance > 1 && value < node.left.data) { return rightRotate(node); } - - // Right Right Case - if (balance < -1 && data > node.right.data) { + if (balance < -1 && value > node.right.data) { return leftRotate(node); } - - // Left Right Case - if (balance > 1 && data > node.left.data) { + if (balance > 1 && value > node.left.data) { node.left = leftRotate(node.left); return rightRotate(node); } - - // Right Left Case - if (balance < -1 && data < node.right.data) { + if (balance < -1 && value < node.right.data) { node.right = rightRotate(node.right); return leftRotate(node); } - return node; } - /** - * Find the node with minimum value - * - * @param node the root of subtree - * @return node with minimum value - */ - private Node minValueNode(Node node) { - Node current = node; - while (current.left != null) { - current = current.left; - } - return current; - } - - /** - * Delete a value from the AVL Tree - * - * @param data the value to delete - */ - public void delete(int data) { - root = deleteRec(root, data); - } - - /** - * Recursive helper method to delete a value and balance the tree - * - * @param root current node - * @param data value to delete - * @return the balanced node - */ - private Node deleteRec(Node root, int data) { - // Perform standard BST delete - if (root == null) { - return root; - } - - if (data < root.data) { - root.left = deleteRec(root.left, data); - } else if (data > root.data) { - root.right = deleteRec(root.right, data); - } else { - // Node with only one child or no child - if ((root.left == null) || (root.right == null)) { - Node temp = root.left != null ? root.left : root.right; - - // No child case - if (temp == null) { - temp = root; - root = null; - } else { - // One child case - root = temp; - } - } else { - // Node with two children - Node temp = minValueNode(root.right); - root.data = temp.data; - root.right = deleteRec(root.right, temp.data); - } - } - - // If tree had only one node - if (root == null) { - return root; - } - - // Update height - root.height = Math.max(height(root.left), height(root.right)) + 1; - - // Get balance factor - int balance = getBalance(root); - - // If unbalanced, there are 4 cases - - // Left Left Case - if (balance > 1 && getBalance(root.left) >= 0) { - return rightRotate(root); - } - - // Left Right Case - if (balance > 1 && getBalance(root.left) < 0) { - root.left = leftRotate(root.left); - return rightRotate(root); - } - - // Right Right Case - if (balance < -1 && getBalance(root.right) <= 0) { - return leftRotate(root); - } - - // Right Left Case - if (balance < -1 && getBalance(root.right) > 0) { - root.right = rightRotate(root.right); - return leftRotate(root); - } - - return root; - } - - /** - * Search for a value in the AVL Tree - * - * @param data the value to search for - * @return true if found, false otherwise - */ - public boolean search(int data) { - return searchRec(root, data); + public boolean search(int value) { + return searchRec(root, value); } - /** - * Recursive helper method to search for a value - * - * @param root current node - * @param data value to search for - * @return true if found, false otherwise - */ - private boolean searchRec(Node root, int data) { - if (root == null) { + private boolean searchRec(Node node, int value) { + if (node == null) { return false; } - - if (root.data == data) { + if (value == node.data) { return true; } - - if (data < root.data) { - return searchRec(root.left, data); - } - - return searchRec(root.right, data); - } - - /** - * Inorder traversal (Left-Root-Right) - * - * @return string representation of inorder traversal - */ - public String inorder() { - StringBuilder result = new StringBuilder(); - inorderRec(root, result); - return result.toString().trim(); - } - - /** - * Recursive helper for inorder traversal - */ - private void inorderRec(Node root, StringBuilder result) { - if (root != null) { - inorderRec(root.left, result); - result.append(root.data).append(" "); - inorderRec(root.right, result); - } - } - - /** - * Preorder traversal (Root-Left-Right) - * - * @return string representation of preorder traversal - */ - public String preorder() { - StringBuilder result = new StringBuilder(); - preorderRec(root, result); - return result.toString().trim(); + return value < node.data ? searchRec(node.left, value) : searchRec(node.right, value); } - /** - * Recursive helper for preorder traversal - */ - private void preorderRec(Node root, StringBuilder result) { - if (root != null) { - result.append(root.data).append(" "); - preorderRec(root.left, result); - preorderRec(root.right, result); - } + public boolean isEmpty() { + return root == null; } - /** - * Get the height of the tree - * - * @return the height of the tree - */ public int getHeight() { return height(root); } - /** - * Check if the tree is empty - * - * @return true if empty, false otherwise - */ - public boolean isEmpty() { - return root == null; - } - - /** - * Check if the tree is balanced (for testing purposes) - * - * @return true if balanced, false otherwise - */ public boolean isBalanced() { return isBalancedRec(root); } - /** - * Recursive helper to check if tree is balanced - */ private boolean isBalancedRec(Node node) { if (node == null) { return true; } - int balance = getBalance(node); + return Math.abs(balance) <= 1 && isBalancedRec(node.left) && isBalancedRec(node.right); + } - if (Math.abs(balance) > 1) { - return false; - } + public String inorder() { + StringBuilder sb = new StringBuilder(); + inorderRec(root, sb); + return sb.toString().trim(); + } - return isBalancedRec(node.left) && isBalancedRec(node.right); + private void inorderRec(Node node, StringBuilder sb) { + if (node != null) { + inorderRec(node.left, sb); + sb.append(node.data).append(" "); + inorderRec(node.right, sb); + } } } diff --git a/src/main/java/com/thealgorithms/datastructures/trees/BinarySearchTree.java b/src/main/java/com/thealgorithms/datastructures/trees/BinarySearchTree.java index 8c85d0dfeb63..1cc50f98cc8c 100644 --- a/src/main/java/com/thealgorithms/datastructures/trees/BinarySearchTree.java +++ b/src/main/java/com/thealgorithms/datastructures/trees/BinarySearchTree.java @@ -1,21 +1,19 @@ package com.thealgorithms.datastructures.trees; - /** - * Binary Search Tree implementation with insert, search, delete, and traversal operations. + * Binary Search Tree implementation * * @author Raghu0703 */ public final class BinarySearchTree { - - /** - * Node class representing each element in the BST - */ static class Node { - int data; - Node left, right; - - public Node(int data) { - this.data = data; + int key; + int value; + Node left; + Node right; + + Node(int key, int value) { + this.key = key; + this.value = value; this.left = null; this.right = null; } @@ -23,224 +21,90 @@ public Node(int data) { private Node root; - /** - * Constructor to initialize empty BST - */ public BinarySearchTree() { this.root = null; } - /** - * Insert a value into the BST - * - * @param data the value to insert - */ - public void insert(int data) { - root = insertRec(root, data); + public void insert(int key, int value) { + root = insertRec(root, key, value); } - /** - * Recursive helper method to insert a value - * - * @param root current node - * @param data value to insert - * @return the modified node - */ - private Node insertRec(Node root, int data) { + private Node insertRec(Node root, int key, int value) { if (root == null) { - root = new Node(data); - return root; + return new Node(key, value); } - - if (data < root.data) { - root.left = insertRec(root.left, data); - } else if (data > root.data) { - root.right = insertRec(root.right, data); + if (key < root.key) { + root.left = insertRec(root.left, key, value); + } else if (key > root.key) { + root.right = insertRec(root.right, key, value); + } else { + root.value = value; } - return root; } - /** - * Search for a value in the BST - * - * @param data the value to search for - * @return true if found, false otherwise - */ - public boolean search(int data) { - return searchRec(root, data); + public Integer search(int key) { + return searchRec(root, key); } - /** - * Recursive helper method to search for a value - * - * @param root current node - * @param data value to search for - * @return true if found, false otherwise - */ - private boolean searchRec(Node root, int data) { + private Integer searchRec(Node root, int key) { if (root == null) { - return false; + return null; } - - if (root.data == data) { - return true; + if (key == root.key) { + return root.value; } - - if (data < root.data) { - return searchRec(root.left, data); - } - - return searchRec(root.right, data); + return key < root.key ? searchRec(root.left, key) : searchRec(root.right, key); } - /** - * Delete a value from the BST - * - * @param data the value to delete - */ - public void delete(int data) { - root = deleteRec(root, data); + public void delete(int key) { + root = deleteRec(root, key); } - /** - * Recursive helper method to delete a value - * - * @param root current node - * @param data value to delete - * @return the modified node - */ - private Node deleteRec(Node root, int data) { + private Node deleteRec(Node root, int key) { if (root == null) { - return root; + return null; } - - if (data < root.data) { - root.left = deleteRec(root.left, data); - } else if (data > root.data) { - root.right = deleteRec(root.right, data); + if (key < root.key) { + root.left = deleteRec(root.left, key); + } else if (key > root.key) { + root.right = deleteRec(root.right, key); } else { - // Node with only one child or no child if (root.left == null) { return root.right; } else if (root.right == null) { return root.left; } - - // Node with two children: Get inorder successor - root.data = minValue(root.right); - root.right = deleteRec(root.right, root.data); + root.key = minValue(root.right); + root.right = deleteRec(root.right, root.key); } - return root; } - /** - * Find the minimum value in a subtree - * - * @param root the root of the subtree - * @return the minimum value - */ private int minValue(Node root) { - int minValue = root.data; + int minv = root.key; while (root.left != null) { - minValue = root.left.data; + minv = root.left.key; root = root.left; } - return minValue; + return minv; } - /** - * Inorder traversal (Left-Root-Right) - * - * @return string representation of inorder traversal - */ - public String inorder() { - StringBuilder result = new StringBuilder(); - inorderRec(root, result); - return result.toString().trim(); - } - - /** - * Recursive helper for inorder traversal - */ - private void inorderRec(Node root, StringBuilder result) { - if (root != null) { - inorderRec(root.left, result); - result.append(root.data).append(" "); - inorderRec(root.right, result); - } - } - - /** - * Preorder traversal (Root-Left-Right) - * - * @return string representation of preorder traversal - */ - public String preorder() { - StringBuilder result = new StringBuilder(); - preorderRec(root, result); - return result.toString().trim(); - } - - /** - * Recursive helper for preorder traversal - */ - private void preorderRec(Node root, StringBuilder result) { - if (root != null) { - result.append(root.data).append(" "); - preorderRec(root.left, result); - preorderRec(root.right, result); - } + public boolean isEmpty() { + return root == null; } - /** - * Postorder traversal (Left-Right-Root) - * - * @return string representation of postorder traversal - */ - public String postorder() { - StringBuilder result = new StringBuilder(); - postorderRec(root, result); - return result.toString().trim(); + public String inorder() { + StringBuilder sb = new StringBuilder(); + inorderRec(root, sb); + return sb.toString().trim(); } - /** - * Recursive helper for postorder traversal - */ - private void postorderRec(Node root, StringBuilder result) { + private void inorderRec(Node root, StringBuilder sb) { if (root != null) { - postorderRec(root.left, result); - postorderRec(root.right, result); - result.append(root.data).append(" "); - } - } - - /** - * Get the height of the tree - * - * @return the height of the tree - */ - public int height() { - return heightRec(root); - } - - /** - * Recursive helper to calculate height - */ - private int heightRec(Node root) { - if (root == null) { - return 0; + inorderRec(root.left, sb); + sb.append(root.key).append(" "); + inorderRec(root.right, sb); } - return 1 + Math.max(heightRec(root.left), heightRec(root.right)); - } - - /** - * Check if the tree is empty - * - * @return true if empty, false otherwise - */ - public boolean isEmpty() { - return root == null; } } diff --git a/src/test/java/com/thealgorithms/datastructures/trees/AVLTreeTest.java b/src/test/java/com/thealgorithms/datastructures/trees/AVLTreeTest.java index 97079ace2deb..be36f36b06dd 100644 --- a/src/test/java/com/thealgorithms/datastructures/trees/AVLTreeTest.java +++ b/src/test/java/com/thealgorithms/datastructures/trees/AVLTreeTest.java @@ -1,6 +1,8 @@ package com.thealgorithms.datastructures.trees; -import static org.junit.jupiter.api.Assertions.*; +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; diff --git a/src/test/java/com/thealgorithms/datastructures/trees/BinarySearchTreeTest.java b/src/test/java/com/thealgorithms/datastructures/trees/BinarySearchTreeTest.java index 6d9ed9dff711..44d2fe4d0c20 100644 --- a/src/test/java/com/thealgorithms/datastructures/trees/BinarySearchTreeTest.java +++ b/src/test/java/com/thealgorithms/datastructures/trees/BinarySearchTreeTest.java @@ -1,6 +1,9 @@ package com.thealgorithms.datastructures.trees; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test;