From f2c22b7f299160ca17bb9a0126b627ec63c6469c Mon Sep 17 00:00:00 2001 From: Sohan Rout Date: Mon, 3 Aug 2026 00:06:36 +0530 Subject: [PATCH] Feat : added new insertion animation in binary tree --- .../trees/bst/insertion/animation.jsx | 323 +++++++++++++++ app/visualizer/trees/bst/insertion/code.js | 129 ++++++ .../trees/bst/insertion/content.jsx | 248 ++++++++++++ app/visualizer/trees/bst/insertion/page.jsx | 104 +++++ app/visualizer/trees/bst/insertion/quiz.jsx | 369 ++++++++++++++++++ lib/modulesMap.js | 1 + public/sitemap-0.xml | 97 ++--- 7 files changed, 1223 insertions(+), 48 deletions(-) create mode 100755 app/visualizer/trees/bst/insertion/animation.jsx create mode 100755 app/visualizer/trees/bst/insertion/code.js create mode 100755 app/visualizer/trees/bst/insertion/content.jsx create mode 100755 app/visualizer/trees/bst/insertion/page.jsx create mode 100755 app/visualizer/trees/bst/insertion/quiz.jsx diff --git a/app/visualizer/trees/bst/insertion/animation.jsx b/app/visualizer/trees/bst/insertion/animation.jsx new file mode 100755 index 0000000..c596c1d --- /dev/null +++ b/app/visualizer/trees/bst/insertion/animation.jsx @@ -0,0 +1,323 @@ +"use client"; +import React, { useState, useRef } 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; + } +} + +const insertNode = (node, value, path) => { + if (!node) return new TreeNode(value); + path.push(node.value); + if (value < node.value) node.left = insertNode(node.left, value, path); + else if (value > node.value) node.right = insertNode(node.right, value, path); + 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 }); + + 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 BstInsertionVisualizer = () => { + const [root, setRoot] = useState(null); + const [inputValue, setInputValue] = useState(""); + const [message, setMessage] = useState("Tree is empty"); + const [highlightPath, setHighlightPath] = useState([]); + const lastInsertedRef = useRef(null); + const highlightTimeoutRef = useRef(null); + + 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; + } + + const path = []; + setRoot((prev) => { + const cloned = prev ? structuredClone(prev) : null; + return insertNode(cloned, value, path); + }); + + lastInsertedRef.current = value; + setMessage( + path.length > 0 + ? `Compared against ${path.join(" → ")}, then inserted ${value}` + : `Inserted ${value} as the root` + ); + setInputValue(""); + + setHighlightPath(path); + if (highlightTimeoutRef.current) clearTimeout(highlightTimeoutRef.current); + highlightTimeoutRef.current = setTimeout(() => setHighlightPath([]), 1100); + }; + + const generateRandomTree = () => { + const size = Math.floor(Math.random() * 5) + 5; + const values = Array.from({ length: size }, () => Math.floor(Math.random() * 100) + 1); + let newRoot = null; + values.forEach((v) => { + newRoot = insertNode(newRoot, v, []); + }); + setRoot(newRoot); + setMessage(`Generated a tree with ${size} random inserts`); + setHighlightPath([]); + }; + + const reset = () => { + setRoot(null); + setInputValue(""); + setMessage("Tree is empty"); + setHighlightPath([]); + }; + + 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 values and watch the comparison path light up before each node lands +

+ +
+ {/* Controls */} +
+
+ setInputValue(e.target.value)} + onKeyDown={(e) => e.key === "Enter" && handleInsert()} + placeholder="Enter a number" + 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" + /> + +
+
+ + +
+
+ + {/* Visualization */} +
+
+ {message} +
+ +
+ {nodes.length > 0 ? ( + + + + + + + + + + + + + + + + {edges.map((edge, i) => { + const midY = (edge.y1 + edge.y2) / 2; + return ( + + ); + })} + + {nodes.map((node, i) => { + const isHighlighted = highlightPath.includes(node.value); + return ( + + {node.isRoot && ( + + )} + {isHighlighted && ( + + )} + + + {node.value} + + + + + d{node.depth} + + + ); + })} + + ) : ( +
+ No tree yet — insert a value or generate a random tree +
+ )} +
+ +
+ + + Internal node + + + + Leaf node + + + + Root + + + + Comparison path + +
+
+
+
+ ); +}; + +export default BstInsertionVisualizer; diff --git a/app/visualizer/trees/bst/insertion/code.js b/app/visualizer/trees/bst/insertion/code.js new file mode 100755 index 0000000..1baf752 --- /dev/null +++ b/app/visualizer/trees/bst/insertion/code.js @@ -0,0 +1,129 @@ +const codeExamples = { + javascript: `// Binary Search Tree node +class TreeNode { + constructor(value) { + this.value = value; + this.left = null; + this.right = null; + } +} + +// Insert a value into the BST +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); + } + // Equal values are ignored (no duplicates) + + return node; +} + +// Usage example +let root = null; +[8, 3, 10, 1, 6, 5].forEach((value) => { + root = insert(root, value); +});`, + + python: `# Binary Search Tree node +class TreeNode: + def __init__(self, value): + self.value = value + self.left = None + self.right = None + +# Insert a value into the BST +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) + # Equal values are ignored (no duplicates) + + return node + +# Usage example +root = None +for value in [8, 3, 10, 1, 6, 5]: + root = insert(root, value)`, + + c: `#include +#include + +typedef struct TreeNode { + int value; + struct TreeNode *left, *right; +} TreeNode; + +TreeNode* newNode(int value) { + TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode)); + node->value = value; + node->left = node->right = NULL; + return node; +} + +// Insert a value into the BST +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); + } + // Equal values are ignored (no duplicates) + + return node; +} + +int main() { + TreeNode* root = NULL; + int values[] = {8, 3, 10, 1, 6, 5}; + for (int i = 0; i < 6; i++) { + root = insert(root, values[i]); + } + return 0; +}`, + + java: `class TreeNode { + int value; + TreeNode left, right; + + TreeNode(int value) { + this.value = value; + } +} + +public class BstInsertion { + + // Insert a value into the BST + 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); + } + // Equal values are ignored (no duplicates) + + return node; + } + + public static void main(String[] args) { + TreeNode root = null; + int[] values = {8, 3, 10, 1, 6, 5}; + for (int value : values) { + root = insert(root, value); + } + } +}`, +}; + +export default codeExamples; diff --git a/app/visualizer/trees/bst/insertion/content.jsx b/app/visualizer/trees/bst/insertion/content.jsx new file mode 100755 index 0000000..ea48d24 --- /dev/null +++ b/app/visualizer/trees/bst/insertion/content.jsx @@ -0,0 +1,248 @@ +"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 WalkthroughDiagram = () => { + const nodes = [ + { id: "8", x: 110, y: 30, highlighted: true }, + { id: "3", x: 70, y: 80, highlighted: true }, + { id: "10", x: 150, y: 80, highlighted: false }, + { id: "1", x: 50, y: 130, highlighted: false }, + { id: "6", x: 90, y: 130, highlighted: true }, + { id: "5", x: 75, y: 175, highlighted: false, isNew: true }, + ]; + const edges = [ + ["8", "3"], + ["8", "10"], + ["3", "1"], + ["3", "6"], + ["6", "5"], + ]; + const byId = Object.fromEntries(nodes.map((n) => [n.id, n])); + + return ( + + {edges.map(([from, to], i) => ( + + ))} + + {nodes.map((n, i) => ( + + {n.highlighted && ( + + )} + + + {n.id} + + + ))} + + ); +}; + +const Content = () => { + const { theme } = useTheme(); + + const paragraphs = [ + `A Binary Search Tree keeps every node's left subtree smaller and its right subtree larger, and insertion is what builds that ordering up one value at a time. To insert a new value, you walk down from the root exactly the way you'd search for it — go left when the value is smaller, go right when it's larger — and the moment you fall off the tree (hit a null pointer), that's where the new node gets attached.`, + `Because every insertion is really just a failed search, the new node always becomes a leaf. Nothing above it has to move or shift — inserting into a BST never requires shuffling existing nodes around, only adding one new connection at the bottom.`, + `Insertion needs O(1) extra space beyond the recursion stack, since it only ever creates a single new node.`, + `The shape you end up with depends entirely on insertion order. Insert already-sorted data and you get a degenerate, linked-list-shaped tree (see Binary Tree Types) with Θ(n) height. Insert in a randomized order and the tree tends to stay close to balanced, keeping height near Θ(log n) — this is exactly why self-balancing trees like AVL exist.`, + ]; + + const walkthrough = [ + { points: "Insert 5 into a tree rooted at 8" }, + { points: "8: 5 < 8 → go left" }, + { points: "3: 5 > 3 → go right" }, + { points: "6: 5 < 6 → go left" }, + { points: "Left of 6 is empty → attach 5 here as a new leaf" }, + ]; + + const algorithm = [ + { points: "Start at the root" }, + { + points: "Compare the new value with the current node:", + subpoints: [ + "If smaller, move to the left child", + "If larger, move to the right child", + "If equal, stop (duplicate — most BSTs ignore or reject it)", + ], + }, + { points: "Repeat until you reach a null (empty) pointer" }, + { points: "Attach the new node there as a leaf" }, + ]; + + const complexity = [ + { points: "Best/Average Case: Roughly balanced tree → O(log n)." }, + { points: "Worst Case: Degenerate/skewed tree → O(n)." }, + ]; + + return ( +
+
+ + +
+
+ {/* What is BST Insertion */} +
+

+ + What is BST Insertion? +

+
+

+ {paragraphs[0]} +

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

+ + How Does It Work? +

+
+
    + {walkthrough.map((item, index) => ( +
  1. + {item.points} +
  2. + ))} +
+
+ + + +
+ + + Comparison path + + + + Newly inserted node + +
+ +

+ {paragraphs[1]} +

+
+ + {/* Algorithm Steps */} +
+

+ + Algorithm Steps +

+
+
    + {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) => n} + maxN={25} + /> +
+ + +
+ + {/* Space Complexity */} +
+

+ + Space Complexity +

+
+

+ {paragraphs[2]} +

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

+ {paragraphs[3]} +

+
+
+
+
+ + +
+ ); +}; + +export default Content; diff --git a/app/visualizer/trees/bst/insertion/page.jsx b/app/visualizer/trees/bst/insertion/page.jsx new file mode 100755 index 0000000..526a718 --- /dev/null +++ b/app/visualizer/trees/bst/insertion/page.jsx @@ -0,0 +1,104 @@ +import Animation from "@/app/visualizer/trees/bst/insertion/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/insertion/quiz"; +import Content from "@/app/visualizer/trees/bst/insertion/content"; +import ModuleCard from "@/app/components/ui/ModuleCard"; +import { MODULE_MAPS } from "@/lib/modulesMap"; + +export const metadata = { + title: "Binary Search Tree Insertion | Step-by-Step Animation & Explanation", + description: + "Learn how insertion works in a Binary Search Tree with an interactive visualizer, a step-by-step comparison-path walkthrough, code examples in JavaScript, C, Python, and Java, and a quiz.", + keywords: [ + "Binary Search Tree Insertion", + "BST Insertion", + "BST Insertion Visualization", + "BST Insertion Algorithm", + "Binary Search Tree Animation", + "Insert into BST", + "BST Insertion in JavaScript", + "BST Insertion in C", + "BST Insertion in Python", + "BST Insertion 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: "Binary Search Tree Insertion Visualization", + }, + ], + }, +}; + +export default function Page() { + const paths = [ + { name: "Home", href: "/" }, + { name: "Visualizer", href: "/visualizer" }, + { name: "Trees : BST Insertion", href: "" }, + ]; + + return ( + <> +
+ +
+ +
+
+ + +
+ +
+ +
+ +
+

+ Test Your Knowledge before moving forward! +

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