mirror of
https://github.com/TheAlgorithms/Java.git
synced 2025-07-06 09:06:51 +08:00
Enhance docs, add tests in AVLTree (#6058)
This commit is contained in:
@ -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)
|
||||||
|
@ -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
|
||||||
Node node = root;
|
*/
|
||||||
Node child = root;
|
public List<Integer> returnBalance() {
|
||||||
|
List<Integer> balances = new ArrayList<>();
|
||||||
|
returnBalance(root, balances);
|
||||||
|
return balances;
|
||||||
|
}
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user