Skip to content

refactor: Enhance docs, add tests in AVLTree #6058

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -912,6 +912,7 @@
* [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)
* 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)
* [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)
Expand Down
144 changes: 82 additions & 62 deletions src/main/java/com/thealgorithms/datastructures/trees/AVLTree.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
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 {

private Node root;

private class Node {

private static class Node {
private int key;
private int balance;
private int height;
Expand All @@ -17,8 +25,18 @@ private class Node {
key = k;
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) {
if (root == null) {
root = new Node(key, null);
Expand All @@ -31,7 +49,6 @@ public boolean insert(int key) {
}

parent = n;

boolean goLeft = n.key > key;
n = goLeft ? n.left : n.right;

Expand All @@ -49,8 +66,32 @@ public boolean insert(int key) {
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) {
if (node.left == null && node.right == null) {
// Leaf node
if (node.parent == null) {
root = null;
} else {
Expand All @@ -64,6 +105,8 @@ private void delete(Node node) {
}
return;
}

// Node has one or two children
Node child;
if (node.left != null) {
child = node.left;
Expand All @@ -80,26 +123,49 @@ private void delete(Node node) {
delete(child);
}

public void delete(int delKey) {
if (root == null) {
return;
/**
* Returns a list of balance factors for each node in the tree.
*
* @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;
}

private void returnBalance(Node n, List<Integer> balances) {
if (n != null) {
returnBalance(n.left, balances);
balances.add(n.getBalance());
returnBalance(n.right, balances);
}
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;
}
/**
* 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) {
setBalance(n);

if (n.balance == -2) {
if (height(n.left.left) >= height(n.left.right)) {
n = rotateRight(n);
Expand Down Expand Up @@ -143,7 +209,6 @@ private Node rotateLeft(Node a) {
}

setBalance(a, b);

return b;
}

Expand All @@ -169,7 +234,6 @@ private Node rotateRight(Node a) {
}

setBalance(a, b);

return b;
}

Expand Down Expand Up @@ -197,53 +261,9 @@ private void setBalance(Node... nodes) {
}
}

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) {
if (node != null) {
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();
}
}
Original file line number Diff line number Diff line change
@@ -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));
}
}