From 58897e04772d7b578b41550bf774f8bcbe10deea Mon Sep 17 00:00:00 2001 From: Sohan Rout Date: Mon, 3 Aug 2026 02:12:06 +0530 Subject: [PATCH 1/2] Feat : added inorder and preorder traversal into the binary tree --- app/visualizer/trees/bst/avl/animation.jsx | 409 ++++++++++++++++++ app/visualizer/trees/bst/avl/code.js | 314 ++++++++++++++ app/visualizer/trees/bst/avl/content.jsx | 274 ++++++++++++ app/visualizer/trees/bst/avl/page.jsx | 110 +++++ app/visualizer/trees/bst/avl/quiz.jsx | 379 ++++++++++++++++ app/visualizer/trees/bst/deletion/page.jsx | 1 + app/visualizer/trees/bst/insertion/page.jsx | 1 + app/visualizer/trees/bst/searching/page.jsx | 1 + .../trees/traversal/in-order/animation.jsx | 386 +++++++++++++++++ .../trees/traversal/in-order/code.js | 133 ++++++ .../trees/traversal/in-order/content.jsx | 216 +++++++++ .../trees/traversal/in-order/page.jsx | 109 +++++ .../trees/traversal/in-order/quiz.jsx | 369 ++++++++++++++++ .../trees/traversal/pre-order/animation.jsx | 386 +++++++++++++++++ .../trees/traversal/pre-order/code.js | 133 ++++++ .../trees/traversal/pre-order/content.jsx | 215 +++++++++ .../trees/traversal/pre-order/page.jsx | 110 +++++ .../trees/traversal/pre-order/quiz.jsx | 364 ++++++++++++++++ .../trees/traversing/in-order/animation.jsx | 405 ----------------- .../trees/traversing/in-order/page.jsx | 43 -- lib/modulesMap.js | 3 + 21 files changed, 3913 insertions(+), 448 deletions(-) create mode 100755 app/visualizer/trees/bst/avl/animation.jsx create mode 100755 app/visualizer/trees/bst/avl/code.js create mode 100755 app/visualizer/trees/bst/avl/content.jsx create mode 100755 app/visualizer/trees/bst/avl/page.jsx create mode 100755 app/visualizer/trees/bst/avl/quiz.jsx create mode 100755 app/visualizer/trees/traversal/in-order/animation.jsx create mode 100755 app/visualizer/trees/traversal/in-order/code.js create mode 100755 app/visualizer/trees/traversal/in-order/content.jsx create mode 100755 app/visualizer/trees/traversal/in-order/page.jsx create mode 100755 app/visualizer/trees/traversal/in-order/quiz.jsx create mode 100755 app/visualizer/trees/traversal/pre-order/animation.jsx create mode 100755 app/visualizer/trees/traversal/pre-order/code.js create mode 100755 app/visualizer/trees/traversal/pre-order/content.jsx create mode 100755 app/visualizer/trees/traversal/pre-order/page.jsx create mode 100755 app/visualizer/trees/traversal/pre-order/quiz.jsx delete mode 100755 app/visualizer/trees/traversing/in-order/animation.jsx delete mode 100755 app/visualizer/trees/traversing/in-order/page.jsx diff --git a/app/visualizer/trees/bst/avl/animation.jsx b/app/visualizer/trees/bst/avl/animation.jsx new file mode 100755 index 0000000..ee61047 --- /dev/null +++ b/app/visualizer/trees/bst/avl/animation.jsx @@ -0,0 +1,409 @@ +"use client"; +import React, { useState } from "react"; +import { gsap } from "gsap"; +import { Plus, Shuffle, RotateCcw } from "lucide-react"; + +class TreeNode { + constructor(value) { + this.value = value; + this.left = null; + this.right = null; + this.height = 1; + } +} + +const height = (node) => (node ? node.height : 0); +const balanceFactor = (node) => (node ? height(node.left) - height(node.right) : 0); +const updateHeight = (node) => { + node.height = 1 + Math.max(height(node.left), height(node.right)); +}; + +const rotateRight = (y) => { + const x = y.left; + const T2 = x.right; + x.right = y; + y.left = T2; + updateHeight(y); + updateHeight(x); + return x; +}; + +const rotateLeft = (x) => { + const y = x.right; + const T2 = y.left; + y.left = x; + x.right = T2; + updateHeight(x); + updateHeight(y); + return y; +}; + +const insertAVL = (node, value, rotations) => { + if (!node) return new TreeNode(value); + if (value < node.value) node.left = insertAVL(node.left, value, rotations); + else if (value > node.value) node.right = insertAVL(node.right, value, rotations); + else return node; + + updateHeight(node); + const bf = balanceFactor(node); + + if (bf > 1 && value < node.left.value) { + rotations.push({ type: "Left-Left", at: node.value }); + return rotateRight(node); + } + if (bf < -1 && value > node.right.value) { + rotations.push({ type: "Right-Right", at: node.value }); + return rotateLeft(node); + } + if (bf > 1 && value > node.left.value) { + rotations.push({ type: "Left-Right", at: node.value }); + node.left = rotateLeft(node.left); + return rotateRight(node); + } + if (bf < -1 && value < node.right.value) { + rotations.push({ type: "Right-Left", at: node.value }); + node.right = rotateRight(node.right); + return rotateLeft(node); + } + + return node; +}; + +const NODE_RADIUS = 22; +const LEVEL_HEIGHT = 78; + +const layoutTree = (node, depth = 0, x = 320, y = 40, nodes = [], edges = []) => { + if (!node) return { nodes, edges }; + const isLeaf = !node.left && !node.right; + const xOffset = Math.max(24, 110 / (depth + 1)); + + nodes.push({ + value: node.value, + x, + y, + depth, + isLeaf, + isRoot: depth === 0, + bf: balanceFactor(node), + }); + + if (node.left) { + const leftX = x - xOffset; + const leftY = y + LEVEL_HEIGHT; + edges.push({ + x1: x, + y1: y + NODE_RADIUS - 2, + x2: leftX, + y2: leftY - NODE_RADIUS + 2, + }); + layoutTree(node.left, depth + 1, leftX, leftY, nodes, edges); + } + if (node.right) { + const rightX = x + xOffset; + const rightY = y + LEVEL_HEIGHT; + edges.push({ + x1: x, + y1: y + NODE_RADIUS - 2, + x2: rightX, + y2: rightY - NODE_RADIUS + 2, + }); + layoutTree(node.right, depth + 1, rightX, rightY, nodes, edges); + } + + return { nodes, edges }; +}; + +const AvlVisualizer = () => { + const [root, setRoot] = useState(null); + const [inputValue, setInputValue] = useState(""); + const [message, setMessage] = useState("Tree is empty"); + const [rotatedValue, setRotatedValue] = useState(null); + const [busy, setBusy] = useState(false); + + const animateDropIn = (el) => { + if (!el || el.dataset.animated) return; + el.dataset.animated = "true"; + gsap.fromTo( + el, + { scale: 0, opacity: 0 }, + { scale: 1, opacity: 1, duration: 0.45, ease: "back.out(1.7)" } + ); + }; + + const handleInsert = () => { + const value = parseInt(inputValue, 10); + if (Number.isNaN(value)) { + setMessage("Please enter a valid number"); + return; + } + if (busy) return; + setBusy(true); + + const rotations = []; + const newRoot = insertAVL(root ? structuredClone(root) : null, value, rotations); + + setTimeout(() => { + setRoot(newRoot); + if (rotations.length > 0) { + const r = rotations[rotations.length - 1]; + setMessage(`Inserted ${value} — ${r.type} case detected, rotated at node ${r.at} to restore balance`); + setRotatedValue(r.at); + setTimeout(() => setRotatedValue(null), 1300); + } else { + setMessage(`Inserted ${value} — tree stayed balanced, no rotation needed`); + } + setInputValue(""); + setBusy(false); + }, 350); + }; + + const generateRandomTree = () => { + const size = Math.floor(Math.random() * 6) + 6; + const values = Array.from({ length: size }, () => Math.floor(Math.random() * 100) + 1); + let newRoot = null; + values.forEach((v) => { + newRoot = insertAVL(newRoot, v, []); + }); + setRoot(newRoot); + setMessage(`Generated a self-balancing tree with ${size} random inserts`); + setRotatedValue(null); + }; + + const reset = () => { + setRoot(null); + setInputValue(""); + setMessage("Tree is empty"); + setRotatedValue(null); + }; + + const { nodes, edges } = root ? layoutTree(root) : { nodes: [], edges: [] }; + + const getSvgDimensions = () => { + if (nodes.length === 0) return { width: 600, height: 220 }; + const xValues = nodes.map((n) => n.x); + const yValues = nodes.map((n) => n.y); + const padding = 40; + return { + width: Math.max(600, Math.max(...xValues) - Math.min(...xValues) + padding * 2), + height: Math.max(220, Math.max(...yValues) + padding * 2), + }; + }; + const dims = getSvgDimensions(); + + return ( +
+

+ Insert a value and watch the tree rotate itself back into balance +

+ +
+ {/* Controls */} +
+
+ setInputValue(e.target.value)} + onKeyDown={(e) => e.key === "Enter" && handleInsert()} + placeholder="Enter a number" + disabled={busy} + className="flex-1 min-w-0 px-3 py-2 text-sm rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-neutral-900 text-gray-800 dark:text-gray-100 placeholder:text-gray-400 dark:placeholder:text-gray-500 outline-none focus:ring-2 focus:ring-blue-500/40 focus:border-blue-500 transition disabled:opacity-50" + /> + +
+
+ + +
+
+ + {/* Visualization */} +
+
+ {message} +
+ +
+ {nodes.length > 0 ? ( + + + + + + + + + + + + + + + + {edges.map((edge, i) => { + const midY = (edge.y1 + edge.y2) / 2; + return ( + + ); + })} + + {nodes.map((node, i) => { + const isRotated = node.value === rotatedValue; + return ( + + {node.isRoot && ( + + )} + {isRotated && ( + + )} + + + {node.value} + + + + + d{node.depth} + + + + + bf {node.bf > 0 ? `+${node.bf}` : node.bf} + + + ); + })} + + ) : ( +
+ No tree yet — insert a value or generate a random tree +
+ )} +
+ +
+ + + Internal node + + + + Leaf node + + + + Root + + + + Just rotated + + + + Balance factor + +
+
+
+
+ ); +}; + +export default AvlVisualizer; diff --git a/app/visualizer/trees/bst/avl/code.js b/app/visualizer/trees/bst/avl/code.js new file mode 100755 index 0000000..36256c0 --- /dev/null +++ b/app/visualizer/trees/bst/avl/code.js @@ -0,0 +1,314 @@ +const codeExamples = { + javascript: `// AVL Tree node +class TreeNode { + constructor(value) { + this.value = value; + this.left = null; + this.right = null; + this.height = 1; + } +} + +const height = (node) => (node ? node.height : 0); +const balanceFactor = (node) => (node ? height(node.left) - height(node.right) : 0); +const updateHeight = (node) => { + node.height = 1 + Math.max(height(node.left), height(node.right)); +}; + +function rotateRight(y) { + const x = y.left; + const T2 = x.right; + x.right = y; + y.left = T2; + updateHeight(y); + updateHeight(x); + return x; // new subtree root +} + +function rotateLeft(x) { + const y = x.right; + const T2 = y.left; + y.left = x; + x.right = T2; + updateHeight(x); + updateHeight(y); + return y; // new subtree root +} + +// Insert a value and rebalance the tree, returning the (possibly new) root +function insert(node, value) { + if (node === null) return new TreeNode(value); + + if (value < node.value) node.left = insert(node.left, value); + else if (value > node.value) node.right = insert(node.right, value); + else return node; // no duplicates + + updateHeight(node); + const bf = balanceFactor(node); + + // Left-Left + if (bf > 1 && value < node.left.value) return rotateRight(node); + // Right-Right + if (bf < -1 && value > node.right.value) return rotateLeft(node); + // Left-Right + if (bf > 1 && value > node.left.value) { + node.left = rotateLeft(node.left); + return rotateRight(node); + } + // Right-Left + if (bf < -1 && value < node.right.value) { + node.right = rotateRight(node.right); + return rotateLeft(node); + } + + return node; +} + +// Usage example +let root = null; +[30, 20, 10, 25, 40, 50].forEach((value) => { + root = insert(root, value); +});`, + + python: `# AVL Tree node +class TreeNode: + def __init__(self, value): + self.value = value + self.left = None + self.right = None + self.height = 1 + +def height(node): + return node.height if node else 0 + +def balance_factor(node): + return height(node.left) - height(node.right) if node else 0 + +def update_height(node): + node.height = 1 + max(height(node.left), height(node.right)) + +def rotate_right(y): + x = y.left + T2 = x.right + x.right = y + y.left = T2 + update_height(y) + update_height(x) + return x # new subtree root + +def rotate_left(x): + y = x.right + T2 = y.left + y.left = x + x.right = T2 + update_height(x) + update_height(y) + return y # new subtree root + +def insert(node, value): + if node is None: + return TreeNode(value) + + if value < node.value: + node.left = insert(node.left, value) + elif value > node.value: + node.right = insert(node.right, value) + else: + return node # no duplicates + + update_height(node) + bf = balance_factor(node) + + # Left-Left + if bf > 1 and value < node.left.value: + return rotate_right(node) + # Right-Right + if bf < -1 and value > node.right.value: + return rotate_left(node) + # Left-Right + if bf > 1 and value > node.left.value: + node.left = rotate_left(node.left) + return rotate_right(node) + # Right-Left + if bf < -1 and value < node.right.value: + node.right = rotate_right(node.right) + return rotate_left(node) + + return node + +# Usage example +root = None +for value in [30, 20, 10, 25, 40, 50]: + root = insert(root, value)`, + + c: `#include +#include + +typedef struct TreeNode { + int value; + int height; + struct TreeNode *left, *right; +} TreeNode; + +int height(TreeNode* node) { + return node ? node->height : 0; +} + +int max(int a, int b) { + return a > b ? a : b; +} + +int balanceFactor(TreeNode* node) { + return node ? height(node->left) - height(node->right) : 0; +} + +TreeNode* newNode(int value) { + TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode)); + node->value = value; + node->left = node->right = NULL; + node->height = 1; + return node; +} + +TreeNode* rotateRight(TreeNode* y) { + TreeNode* x = y->left; + TreeNode* T2 = x->right; + x->right = y; + y->left = T2; + y->height = 1 + max(height(y->left), height(y->right)); + x->height = 1 + max(height(x->left), height(x->right)); + return x; // new subtree root +} + +TreeNode* rotateLeft(TreeNode* x) { + TreeNode* y = x->right; + TreeNode* T2 = y->left; + y->left = x; + x->right = T2; + x->height = 1 + max(height(x->left), height(x->right)); + y->height = 1 + max(height(y->left), height(y->right)); + return y; // new subtree root +} + +TreeNode* insert(TreeNode* node, int value) { + if (node == NULL) return newNode(value); + + if (value < node->value) node->left = insert(node->left, value); + else if (value > node->value) node->right = insert(node->right, value); + else return node; // no duplicates + + node->height = 1 + max(height(node->left), height(node->right)); + int bf = balanceFactor(node); + + // Left-Left + if (bf > 1 && value < node->left->value) return rotateRight(node); + // Right-Right + if (bf < -1 && value > node->right->value) return rotateLeft(node); + // Left-Right + if (bf > 1 && value > node->left->value) { + node->left = rotateLeft(node->left); + return rotateRight(node); + } + // Right-Left + if (bf < -1 && value < node->right->value) { + node->right = rotateRight(node->right); + return rotateLeft(node); + } + + return node; +} + +int main() { + TreeNode* root = NULL; + int values[] = {30, 20, 10, 25, 40, 50}; + for (int i = 0; i < 6; i++) { + root = insert(root, values[i]); + } + printf("AVL tree built, root value: %d\\n", root->value); + return 0; +}`, + + java: `class TreeNode { + int value, height; + TreeNode left, right; + + TreeNode(int value) { + this.value = value; + this.height = 1; + } +} + +public class AvlTree { + + static int height(TreeNode node) { + return node == null ? 0 : node.height; + } + + static int balanceFactor(TreeNode node) { + return node == null ? 0 : height(node.left) - height(node.right); + } + + static void updateHeight(TreeNode node) { + node.height = 1 + Math.max(height(node.left), height(node.right)); + } + + static TreeNode rotateRight(TreeNode y) { + TreeNode x = y.left; + TreeNode T2 = x.right; + x.right = y; + y.left = T2; + updateHeight(y); + updateHeight(x); + return x; // new subtree root + } + + static TreeNode rotateLeft(TreeNode x) { + TreeNode y = x.right; + TreeNode T2 = y.left; + y.left = x; + x.right = T2; + updateHeight(x); + updateHeight(y); + return y; // new subtree root + } + + static TreeNode insert(TreeNode node, int value) { + if (node == null) return new TreeNode(value); + + if (value < node.value) node.left = insert(node.left, value); + else if (value > node.value) node.right = insert(node.right, value); + else return node; // no duplicates + + updateHeight(node); + int bf = balanceFactor(node); + + // Left-Left + if (bf > 1 && value < node.left.value) return rotateRight(node); + // Right-Right + if (bf < -1 && value > node.right.value) return rotateLeft(node); + // Left-Right + if (bf > 1 && value > node.left.value) { + node.left = rotateLeft(node.left); + return rotateRight(node); + } + // Right-Left + if (bf < -1 && value < node.right.value) { + node.right = rotateRight(node.right); + return rotateLeft(node); + } + + return node; + } + + public static void main(String[] args) { + TreeNode root = null; + int[] values = {30, 20, 10, 25, 40, 50}; + for (int value : values) { + root = insert(root, value); + } + System.out.println("AVL tree built, root value: " + root.value); + } +}`, +}; + +export default codeExamples; diff --git a/app/visualizer/trees/bst/avl/content.jsx b/app/visualizer/trees/bst/avl/content.jsx new file mode 100755 index 0000000..db29c3c --- /dev/null +++ b/app/visualizer/trees/bst/avl/content.jsx @@ -0,0 +1,274 @@ +"use client"; +import ComplexityGraph from "@/app/components/ui/graph"; +import { useTheme } from "@/app/contexts/ThemeContext"; +import DailyDSAEmbed from "@/app/components/ui/DailyDSAEmbed"; +import NewsletterEmbed from "@/app/components/ui/NewsletterEmbed"; +import InContentAd from "@/app/components/ads/InContentAd"; +import { motion } from "framer-motion"; + +const MiniTree = ({ nodes, edges, keyPrefix }) => { + const byId = Object.fromEntries(nodes.map((n) => [n.id, n])); + return ( + + {edges.map(([from, to], i) => ( + + ))} + {nodes.map((n, i) => ( + + {n.ring && ( + + )} + + + {n.id} + + + ))} + + ); +}; + +const Content = () => { + const { theme } = useTheme(); + + const paragraphs = [ + `A plain BST offers no guarantee about its shape — insert values in the wrong order and it degenerates into a linked list with O(n) operations (see BST Insertion). An AVL tree fixes this by adding one rule on top of the normal BST ordering: for every node, the heights of its left and right subtrees may differ by at most 1. This is called the balance factor, and it's recalculated bottom-up after every insertion or deletion.`, + `Whenever an insertion pushes a node's balance factor to +2 or -2, the tree is out of balance and needs a rotation — a local rearrangement of a few pointers that restores the height rule without breaking the BST ordering. There are four possible imbalance shapes, and each has a matching fix: a single rotation for the two "straight-line" cases (Left-Left, Right-Right), and a double rotation for the two "zig-zag" cases (Left-Right, Right-Left).`, + `Because at most O(log n) ancestors need their balance factor rechecked after an insertion, and each rotation touches only a constant number of pointers, a single insertion or deletion does at most one rotation (single or double) to restore balance — so the extra bookkeeping AVL trees do is cheap.`, + `The payoff is that an AVL tree's height is always O(log n), no matter what order values are inserted in — unlike a plain BST, it can never degrade into a skewed shape. This makes search, insertion, and deletion all worst-case O(log n), not just average-case.`, + ]; + + const cases = [ + { title: "Left-Left", body: "A left-heavy node whose left child is also left-heavy. Fixed with a single right rotation." }, + { title: "Right-Right", body: "A right-heavy node whose right child is also right-heavy. Fixed with a single left rotation." }, + { title: "Left-Right", body: "A left-heavy node whose left child is right-heavy (zig-zag). Fixed by rotating the left child left, then the node right." }, + { title: "Right-Left", body: "A right-heavy node whose right child is left-heavy (zig-zag). Fixed by rotating the right child right, then the node left." }, + ]; + + const beforeNodes = [ + { id: "30", x: 140, y: 30, fill: "#dc2626", stroke: "#b91c1c", ring: "#ef4444" }, + { id: "20", x: 90, y: 80, fill: "#8b5cf6", stroke: "#7c3aed", ring: "#8b5cf6" }, + { id: "10", x: 60, y: 130 }, + ]; + const beforeEdges = [ + ["30", "20"], + ["20", "10"], + ]; + + const afterNodes = [ + { id: "20", x: 100, y: 30, fill: "#10b981", stroke: "#059669" }, + { id: "10", x: 60, y: 80 }, + { id: "30", x: 140, y: 80 }, + ]; + const afterEdges = [ + ["20", "10"], + ["20", "30"], + ]; + + const algorithm = [ + { points: "Insert the value the normal BST way (compare and recurse left/right)" }, + { points: "On the way back up the recursion, update each ancestor's height" }, + { points: "Compute the balance factor: height(left) − height(right)" }, + { + points: "If the balance factor is +2 or -2, identify which of the 4 cases applies and rotate:", + subpoints: [ + "Left-Left → single right rotation", + "Right-Right → single left rotation", + "Left-Right → left rotation on the left child, then right rotation on the node", + "Right-Left → right rotation on the right child, then left rotation on the node", + ], + }, + { points: "Return the (possibly new) subtree root to the parent call" }, + ]; + + const complexity = [ + { points: "Best/Average/Worst Case: Height is always O(log n) → O(log n)." }, + ]; + + return ( +
+
+ + +
+
+ {/* What is an AVL Tree */} +
+

+ + What is an AVL Tree? +

+
+

+ {paragraphs[0]} +

+
+
+ + {/* How Does It Work */} +
+

+ + How Do Rotations Work? +

+
+

+ {paragraphs[1]} +

+
+ +
+ {cases.map((c) => ( +
+
+ {c.title} +
+
{c.body}
+
+ ))} +
+ +
+
+
+ Inserting 10 unbalances 30 (Left-Left) +
+ +
+
+
+ A right rotation at 30 restores balance +
+ +
+
+ +
+ + + Unbalanced node (bf ±2) + + + + Pivot that becomes the new subtree root + + + + New subtree root after rotation + +
+
+ + {/* Algorithm Steps */} +
+

+ + Algorithm Steps (Insertion) +

+
+
    + {algorithm.map((item, index) => ( +
  1. + {item.points} + {item.subpoints && ( +
      + {item.subpoints.map((subitem, subindex) => ( +
    • + {subitem} +
    • + ))} +
    + )} +
  2. + ))} +
+
+
+ + {/* Time Complexity */} +
+

+ + Time Complexity +

+
+
    + {complexity.map((item, index) => ( +
  • + + {item.points.split(":")[0]}: + + {item.points.split(":")[1]} +
  • + ))} +
+
+ +
+ Math.log2(n)} + averageCase={(n) => Math.log2(n)} + worstCase={(n) => Math.log2(n)} + maxN={25} + /> +
+ + +
+ + {/* Space Complexity */} +
+

+ + Space Complexity +

+
+

+ {paragraphs[2]} +

+
+
+ + {/* Additional Info */} +
+
+
+

+ {paragraphs[3]} +

+
+
+
+
+ + +
+ ); +}; + +export default Content; diff --git a/app/visualizer/trees/bst/avl/page.jsx b/app/visualizer/trees/bst/avl/page.jsx new file mode 100755 index 0000000..4ad89fd --- /dev/null +++ b/app/visualizer/trees/bst/avl/page.jsx @@ -0,0 +1,110 @@ +import Animation from "@/app/visualizer/trees/bst/avl/animation"; +import Navbar from "@/app/components/navbarinner"; +import ModuleHeader from "@/app/components/modules/Header"; +import Footer from "@/app/components/footer"; +import BackToTop from "@/app/components/ui/backtotop"; +import ExploreOther from "@/app/components/ui/exploreOther"; +import CodeBlock from "@/app/components/modules/CodeBlock"; +import codeExamples from "./code"; +import Quiz from "@/app/visualizer/trees/bst/avl/quiz"; +import Content from "@/app/visualizer/trees/bst/avl/content"; +import ModuleCard from "@/app/components/ui/ModuleCard"; +import { MODULE_MAPS } from "@/lib/modulesMap"; + +export const metadata = { + title: "AVL Tree Balancing | Step-by-Step Rotation Animation & Explanation", + description: + "Learn how AVL trees self-balance a Binary Search Tree with rotations — Left-Left, Right-Right, Left-Right, Right-Left — with an interactive visualizer, code examples in JavaScript, C, Python, and Java, and a quiz.", + keywords: [ + "AVL Tree", + "AVL Tree Balancing", + "AVL Rotation", + "Self-Balancing Binary Search Tree", + "Left-Left Rotation", + "Right-Right Rotation", + "Left-Right Rotation", + "Right-Left Rotation", + "AVL Tree Visualization", + "AVL Tree in JavaScript", + "AVL Tree in C", + "AVL Tree in Python", + "AVL Tree in Java", + "DSA Binary Search Tree", + "Learn Binary Search Trees", + "BST Quiz", + ], + robots: "index, follow", + openGraph: { + images: [ + { + url: "/og.png", + width: 1200, + height: 630, + alt: "AVL Tree Balancing Visualization", + }, + ], + }, +}; + +export default function Page() { + const paths = [ + { name: "Home", href: "/" }, + { name: "Visualizer", href: "/visualizer" }, + { name: "Trees : AVL Balancing", href: "" }, + ]; + + return ( + <> +
+ +
+ +
+
+ + +
+ +
+ +
+ +
+

+ Test Your Knowledge before moving forward! +

+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+ + +