Enhance docs, add tests in AVLTree (#6058)

This commit is contained in:
Hardik Pawar
2024-10-29 23:32:33 +05:30
committed by GitHub
parent e94be712df
commit b31bc86192
3 changed files with 185 additions and 63 deletions

View File

@ -912,6 +912,7 @@
* [StackArrayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/stacks/StackArrayTest.java) * [StackArrayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/stacks/StackArrayTest.java)
* [StackOfLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/stacks/StackOfLinkedListTest.java) * [StackOfLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/stacks/StackOfLinkedListTest.java)
* trees * trees
* [AVLTreeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/AVLTreeTest.java)
* [BinaryTreeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/BinaryTreeTest.java) * [BinaryTreeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/BinaryTreeTest.java)
* [BoundaryTraversalTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/BoundaryTraversalTest.java) * [BoundaryTraversalTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/BoundaryTraversalTest.java)
* [BSTFromSortedArrayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/BSTFromSortedArrayTest.java) * [BSTFromSortedArrayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/BSTFromSortedArrayTest.java)

View File

@ -1,11 +1,19 @@
package com.thealgorithms.datastructures.trees; 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.
*/
public class AVLTree { public class AVLTree {
private Node root; private Node root;
private class Node { private static class Node {
private int key; private int key;
private int balance; private int balance;
private int height; private int height;
@ -17,8 +25,18 @@ public class AVLTree {
key = k; key = k;
parent = p; parent = p;
} }
public Integer getBalance() {
return balance;
}
} }
/**
* 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
*/
public boolean insert(int key) { public boolean insert(int key) {
if (root == null) { if (root == null) {
root = new Node(key, null); root = new Node(key, null);
@ -31,7 +49,6 @@ public class AVLTree {
} }
parent = n; parent = n;
boolean goLeft = n.key > key; boolean goLeft = n.key > key;
n = goLeft ? n.left : n.right; n = goLeft ? n.left : n.right;
@ -49,8 +66,32 @@ public class AVLTree {
return true; return true;
} }
/**
* Deletes a key from the AVL tree.
*
* @param delKey the key to be deleted
*/
public void delete(int delKey) {
if (root == null) {
return;
}
// 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;
}
}
}
private void delete(Node node) { private void delete(Node node) {
if (node.left == null && node.right == null) { if (node.left == null && node.right == null) {
// Leaf node
if (node.parent == null) { if (node.parent == null) {
root = null; root = null;
} else { } else {
@ -64,6 +105,8 @@ public class AVLTree {
} }
return; return;
} }
// Node has one or two children
Node child; Node child;
if (node.left != null) { if (node.left != null) {
child = node.left; child = node.left;
@ -80,26 +123,49 @@ public class AVLTree {
delete(child); delete(child);
} }
public void delete(int delKey) { /**
if (root == null) { * Returns a list of balance factors for each node in the tree.
return; *
* @return a list of integers representing the balance factors of the nodes
*/
public List<Integer> returnBalance() {
List<Integer> balances = new ArrayList<>();
returnBalance(root, balances);
return balances;
} }
Node node = root;
Node child = root;
while (child != null) { private void returnBalance(Node n, List<Integer> balances) {
node = child; if (n != null) {
child = delKey >= node.key ? node.right : node.left; returnBalance(n.left, balances);
if (delKey == node.key) { balances.add(n.getBalance());
delete(node); returnBalance(n.right, balances);
return;
} }
} }
/**
* Searches for a key in the AVL tree.
*
* @param key the key to be searched
* @return true if the key is found, false otherwise
*/
public boolean search(int key) {
Node result = searchHelper(this.root, key);
return result != null;
}
private Node searchHelper(Node root, int key) {
if (root == null || root.key == key) {
return root;
}
if (root.key > key) {
return searchHelper(root.left, key);
}
return searchHelper(root.right, key);
} }
private void rebalance(Node n) { private void rebalance(Node n) {
setBalance(n); setBalance(n);
if (n.balance == -2) { if (n.balance == -2) {
if (height(n.left.left) >= height(n.left.right)) { if (height(n.left.left) >= height(n.left.right)) {
n = rotateRight(n); n = rotateRight(n);
@ -143,7 +209,6 @@ public class AVLTree {
} }
setBalance(a, b); setBalance(a, b);
return b; return b;
} }
@ -169,7 +234,6 @@ public class AVLTree {
} }
setBalance(a, b); setBalance(a, b);
return b; return b;
} }
@ -197,53 +261,9 @@ public class AVLTree {
} }
} }
public void printBalance() {
printBalance(root);
}
private void printBalance(Node n) {
if (n != null) {
printBalance(n.left);
System.out.printf("%s ", n.balance);
printBalance(n.right);
}
}
private void reheight(Node node) { private void reheight(Node node) {
if (node != null) { if (node != null) {
node.height = 1 + Math.max(height(node.left), height(node.right)); node.height = 1 + Math.max(height(node.left), height(node.right));
} }
} }
public boolean search(int key) {
Node result = searchHelper(this.root, key);
return result != null;
}
private Node searchHelper(Node root, int key) {
// root is null or key is present at root
if (root == null || root.key == key) {
return root;
}
// key is greater than root's key
if (root.key > key) {
return searchHelper(root.left, key); // call the function on the node's left child
}
// key is less than root's key then
// call the function on the node's right child as it is greater
return searchHelper(root.right, key);
}
public static void main(String[] args) {
AVLTree tree = new AVLTree();
System.out.println("Inserting values 1 to 10");
for (int i = 1; i < 10; i++) {
tree.insert(i);
}
System.out.print("Printing balance: ");
tree.printBalance();
}
} }

View File

@ -0,0 +1,101 @@
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 org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class AVLTreeTest {
private AVLTree avlTree;
@BeforeEach
public void setUp() {
avlTree = new AVLTree();
}
@Test
public void testInsert() {
assertTrue(avlTree.insert(10));
assertTrue(avlTree.insert(20));
assertTrue(avlTree.insert(5));
assertFalse(avlTree.insert(10)); // Duplicate
}
@Test
public void testSearch() {
avlTree.insert(15);
avlTree.insert(25);
assertTrue(avlTree.search(15));
assertFalse(avlTree.search(30)); // Not in the tree
}
@Test
public void testDeleteLeafNode() {
avlTree.insert(10);
avlTree.insert(20);
avlTree.insert(30);
avlTree.delete(30);
assertFalse(avlTree.search(30));
}
@Test
public void testDeleteNodeWithOneChild() {
avlTree.insert(20);
avlTree.insert(10);
avlTree.insert(30);
avlTree.delete(10);
assertFalse(avlTree.search(10));
}
@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));
}
@Test
public void testReturnBalance() {
avlTree.insert(10);
avlTree.insert(20);
avlTree.insert(5);
List<Integer> 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
}
@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));
}
@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));
}
}