Skip to content

Commit f3d7f0e

Browse files
Added AVL tree implementation with detailed comments
1 parent c7ccfd6 commit f3d7f0e

File tree

2 files changed

+612
-0
lines changed

2 files changed

+612
-0
lines changed
Lines changed: 367 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,367 @@
1+
package com.thealgorithms.tree;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import java.util.NoSuchElementException;
6+
7+
/**
8+
* AVL (Adelson-Velsky and Landis) Tree is a self-balancing Binary Search Tree.
9+
* Operations supported:
10+
* - insert, delete, search
11+
* - inorder, preorder, postorder traversal
12+
* - findMin(), findMax()
13+
*
14+
* Properties:
15+
* - For every node: |height(left) - height(right)| <= 1
16+
* - Maintains O(log n) time complexity for insert/delete/search
17+
*/
18+
public class AVL {
19+
20+
/**
21+
* Inner class to represent a node in AVL Tree
22+
*/
23+
private static class Node {
24+
int key;
25+
int height;
26+
Node left;
27+
Node right;
28+
29+
Node(int key) {
30+
this.key = key;
31+
this.height = 1; // New node starts as a leaf node with height = 1
32+
}
33+
}
34+
35+
// Root node of the AVL Tree
36+
private Node root;
37+
38+
// Constructor
39+
public AVL() {
40+
root = null;
41+
}
42+
43+
/* ======================== PUBLIC METHODS ======================== */
44+
45+
/** Insert a key into the AVL Tree */
46+
public void insert(int key) {
47+
root = insertRecursive(root, key);
48+
}
49+
50+
/** Delete a key from the AVL Tree */
51+
public void delete(int key) {
52+
root = deleteRecursive(root, key);
53+
}
54+
55+
/** Search a key in the AVL Tree */
56+
public boolean search(int key) {
57+
return searchRecursive(root, key);
58+
}
59+
60+
/** Return the smallest key in the AVL Tree */
61+
public int findMin() {
62+
if (root == null) throw new NoSuchElementException("AVL Tree is empty");
63+
return findMinNode(root).key;
64+
}
65+
66+
/** Return the largest key in the AVL Tree */
67+
public int findMax() {
68+
if (root == null) throw new NoSuchElementException("AVL Tree is empty");
69+
Node cur = root;
70+
while (cur.right != null) cur = cur.right;
71+
return cur.key;
72+
}
73+
74+
/** Print nodes in Inorder (sorted order) */
75+
public void printInorder() {
76+
System.out.print("Inorder: ");
77+
printInorderRecursive(root);
78+
System.out.println();
79+
}
80+
81+
/** Print nodes in Preorder (Root → Left → Right) */
82+
public void printPreorder() {
83+
System.out.print("Preorder: ");
84+
printPreorderRecursive(root);
85+
System.out.println();
86+
}
87+
88+
/** Print nodes in Postorder (Left → Right → Root) */
89+
public void printPostorder() {
90+
System.out.print("Postorder: ");
91+
printPostorderRecursive(root);
92+
System.out.println();
93+
}
94+
95+
/** Return Inorder list (useful for testing) */
96+
public List<Integer> inorderList() {
97+
List<Integer> res = new ArrayList<>();
98+
inorderToList(root, res);
99+
return res;
100+
}
101+
102+
/** Return Preorder list (useful for testing) */
103+
public List<Integer> preorderList() {
104+
List<Integer> res = new ArrayList<>();
105+
preorderToList(root, res);
106+
return res;
107+
}
108+
109+
/** Return Postorder list (useful for testing) */
110+
public List<Integer> postorderList() {
111+
List<Integer> res = new ArrayList<>();
112+
postorderToList(root, res);
113+
return res;
114+
}
115+
116+
117+
/**
118+
* Recursive insert:
119+
* 1. Insert key like a normal BST
120+
* 2. Update height of current node
121+
* 3. Balance the node if it became unbalanced
122+
*/
123+
private Node insertRecursive(Node node, int key) {
124+
// Step 1: Perform standard BST insert
125+
if (node == null) return new Node(key);
126+
127+
if (key < node.key)
128+
node.left = insertRecursive(node.left, key);
129+
else if (key > node.key)
130+
node.right = insertRecursive(node.right, key);
131+
else
132+
return node; // Duplicates not allowed
133+
134+
// Step 2: Update height of ancestor node
135+
updateHeight(node);
136+
137+
// Step 3: Balance the node and return new root
138+
return balanceNode(node);
139+
}
140+
141+
/**
142+
* Recursive delete:
143+
* 1. Perform normal BST delete
144+
* 2. Update height of current node
145+
* 3. Balance it if necessary
146+
*/
147+
private Node deleteRecursive(Node node, int key) {
148+
if (node == null) return null;
149+
150+
// Step 1: Perform BST delete
151+
if (key < node.key)
152+
node.left = deleteRecursive(node.left, key);
153+
else if (key > node.key)
154+
node.right = deleteRecursive(node.right, key);
155+
else {
156+
// Node found
157+
if (node.left == null || node.right == null) {
158+
Node temp = (node.left != null) ? node.left : node.right;
159+
160+
// No child case
161+
if (temp == null) {
162+
node = null;
163+
} else {
164+
node = temp;
165+
}
166+
} else {
167+
// Node with two children → get inorder successor
168+
Node successor = findMinNode(node.right);
169+
node.key = successor.key;
170+
node.right = deleteRecursive(node.right, successor.key);
171+
}
172+
}
173+
174+
// If tree had only one node
175+
if (node == null) return null;
176+
177+
// Step 2: Update height
178+
updateHeight(node);
179+
180+
// Step 3: Rebalance node
181+
return balanceNode(node);
182+
}
183+
184+
/** Recursive search like normal BST */
185+
private boolean searchRecursive(Node node, int key) {
186+
if (node == null) return false;
187+
if (key == node.key) return true;
188+
return key < node.key ? searchRecursive(node.left, key) : searchRecursive(node.right, key);
189+
}
190+
191+
/** Find node with minimum key */
192+
private Node findMinNode(Node node) {
193+
Node cur = node;
194+
while (cur.left != null) cur = cur.left;
195+
return cur;
196+
}
197+
198+
/* ======================== ROTATIONS & BALANCING ======================== */
199+
200+
/** Right rotation (used in LL or LR imbalance) */
201+
private Node rightRotate(Node y) {
202+
Node x = y.left;
203+
Node T2 = x.right;
204+
205+
// Perform rotation
206+
x.right = y;
207+
y.left = T2;
208+
209+
// Update heights
210+
updateHeight(y);
211+
updateHeight(x);
212+
213+
return x; // New root
214+
}
215+
216+
/** Left rotation (used in RR or RL imbalance) */
217+
private Node leftRotate(Node x) {
218+
Node y = x.right;
219+
Node T2 = y.left;
220+
221+
// Perform rotation
222+
y.left = x;
223+
x.right = T2;
224+
225+
// Update heights
226+
updateHeight(x);
227+
updateHeight(y);
228+
229+
return y; // New root
230+
}
231+
232+
/**
233+
* Balances a node by checking its balance factor:
234+
*
235+
* - If > 1 → left heavy
236+
* - If < -1 → right heavy
237+
*
238+
* Depending on the case, we do:
239+
* - LL → Right Rotate
240+
* - RR → Left Rotate
241+
* - LR → Left Rotate child + Right Rotate
242+
* - RL → Right Rotate child + Left Rotate
243+
*/
244+
private Node balanceNode(Node node) {
245+
int balance = getBalance(node);
246+
247+
// Case 1: Left Left (LL)
248+
if (balance > 1 && getBalance(node.left) >= 0)
249+
return rightRotate(node);
250+
251+
// Case 2: Left Right (LR)
252+
if (balance > 1 && getBalance(node.left) < 0) {
253+
node.left = leftRotate(node.left);
254+
return rightRotate(node);
255+
}
256+
257+
// Case 3: Right Right (RR)
258+
if (balance < -1 && getBalance(node.right) <= 0)
259+
return leftRotate(node);
260+
261+
// Case 4: Right Left (RL)
262+
if (balance < -1 && getBalance(node.right) > 0) {
263+
node.right = rightRotate(node.right);
264+
return leftRotate(node);
265+
}
266+
267+
return node; // Already balanced
268+
}
269+
270+
/* ======================== HELPER FUNCTIONS ======================== */
271+
272+
/** Returns height of a node */
273+
private int height(Node node) {
274+
return node == null ? 0 : node.height;
275+
}
276+
277+
/** Updates height of a node based on its children */
278+
private void updateHeight(Node node) {
279+
node.height = 1 + Math.max(height(node.left), height(node.right));
280+
}
281+
282+
/** Calculates balance factor = height(left) - height(right) */
283+
private int getBalance(Node node) {
284+
return node == null ? 0 : height(node.left) - height(node.right);
285+
}
286+
287+
/* ======================== TRAVERSALS ======================== */
288+
289+
private void printInorderRecursive(Node node) {
290+
if (node == null) return;
291+
printInorderRecursive(node.left);
292+
System.out.print(node.key + " ");
293+
printInorderRecursive(node.right);
294+
}
295+
296+
private void printPreorderRecursive(Node node) {
297+
if (node == null) return;
298+
System.out.print(node.key + " ");
299+
printPreorderRecursive(node.left);
300+
printPreorderRecursive(node.right);
301+
}
302+
303+
private void printPostorderRecursive(Node node) {
304+
if (node == null) return;
305+
printPostorderRecursive(node.left);
306+
printPostorderRecursive(node.right);
307+
System.out.print(node.key + " ");
308+
}
309+
310+
private void inorderToList(Node node, List<Integer> out) {
311+
if (node == null) return;
312+
inorderToList(node.left, out);
313+
out.add(node.key);
314+
inorderToList(node.right, out);
315+
}
316+
317+
private void preorderToList(Node node, List<Integer> out) {
318+
if (node == null) return;
319+
out.add(node.key);
320+
preorderToList(node.left, out);
321+
preorderToList(node.right, out);
322+
}
323+
324+
private void postorderToList(Node node, List<Integer> out) {
325+
if (node == null) return;
326+
postorderToList(node.left, out);
327+
postorderToList(node.right, out);
328+
out.add(node.key);
329+
}
330+
331+
public static void main(String[] args) {
332+
AVL avl = new AVL();
333+
334+
int[] values = {30, 20, 40, 10, 25, 35, 50, 5, 15, 27, 45, 60};
335+
336+
// Insert all values one by one
337+
for (int v : values) avl.insert(v);
338+
339+
// Display traversals
340+
avl.printInorder(); // should show sorted order
341+
avl.printPreorder();
342+
avl.printPostorder();
343+
344+
// Display traversal lists
345+
System.out.println("Inorder List: " + avl.inorderList());
346+
System.out.println("Preorder List: " + avl.preorderList());
347+
System.out.println("Postorder List: " + avl.postorderList());
348+
349+
// Search examples
350+
System.out.println("Search 27: " + avl.search(27)); // true
351+
System.out.println("Search 99: " + avl.search(99)); // false
352+
353+
// Min and Max
354+
System.out.println("Min: " + avl.findMin());
355+
System.out.println("Max: " + avl.findMax());
356+
357+
// Delete operations and show tree after each
358+
avl.delete(10);
359+
System.out.println("After deleting 10: " + avl.inorderList());
360+
361+
avl.delete(30);
362+
System.out.println("After deleting 30: " + avl.inorderList());
363+
364+
avl.delete(40);
365+
System.out.println("After deleting 40: " + avl.inorderList());
366+
}
367+
}

0 commit comments

Comments
 (0)