-
-
-
-
-
-
-
-
-
-
-
-
-
- Selection Sort
-
-
-
-
-
-
-
-
-
-
-
-
- Test Your Knowledge before moving forward!
-
-
-
-
-
-
-
-
-
-
-
-
-
- >
+
}
+ content={
}
+ code={
}
+ quiz={
}
+ moduleCard={
+
+ }
+ exploreOther={
+
+ }
+ />
);
}
diff --git a/app/visualizer/stack/implementation/usingArray/page.jsx b/app/visualizer/stack/implementation/usingArray/page.jsx
index 91cbeea4d..284c34614 100755
--- a/app/visualizer/stack/implementation/usingArray/page.jsx
+++ b/app/visualizer/stack/implementation/usingArray/page.jsx
@@ -1,13 +1,12 @@
-import Navbar from "@/app/components/navbarinner";
-import Breadcrumbs from "@/app/components/ui/Breadcrumbs";
import ArticleActions from "@/app/components/ui/ArticleActions";
import Content from "@/app/visualizer/stack/implementation/usingArray/content";
import Code from "@/app/visualizer/stack/implementation/usingArray/codeBlock";
import ModuleCard from "@/app/components/ui/ModuleCard";
-import { MODULE_MAPS } from "@/lib/modulesMap";
-import Footer from "@/app/components/footer";
import ExploreOther from "@/app/components/ui/exploreOther";
-import BackToTopButton from "@/app/components/ui/backtotop";
+import VisualizerPageLayout, {
+ createVisualizerPaths,
+} from "@/app/visualizer/components/VisualizerPageLayout";
+import { MODULE_MAPS } from "@/lib/modulesMap";
export const metadata = {
title:
@@ -43,62 +42,26 @@ export const metadata = {
};
export default function Page() {
- const paths = [
- { name: "Home", href: "/" },
- { name: "Visualizer", href: "/visualizer" },
- { name: "Stack : Implementation Using Array", href: "" },
- ];
-
return (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
-
- Implementation Using Array
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- >
+
}
+ content={
}
+ code={
}
+ moduleCard={
+
+ }
+ exploreOther={
+
+ }
+ />
);
}
diff --git a/app/visualizer/stack/implementation/usingLinkedList/page.jsx b/app/visualizer/stack/implementation/usingLinkedList/page.jsx
index 06f2889e4..f4a58a59c 100755
--- a/app/visualizer/stack/implementation/usingLinkedList/page.jsx
+++ b/app/visualizer/stack/implementation/usingLinkedList/page.jsx
@@ -1,13 +1,12 @@
-import Navbar from "@/app/components/navbarinner";
-import Breadcrumbs from "@/app/components/ui/Breadcrumbs";
import ArticleActions from "@/app/components/ui/ArticleActions";
import Content from "@/app/visualizer/stack/implementation/usingLinkedList/content";
import Code from "@/app/visualizer/stack/implementation/usingLinkedList/codeBlock";
import ModuleCard from "@/app/components/ui/ModuleCard";
-import { MODULE_MAPS } from "@/lib/modulesMap";
-import Footer from "@/app/components/footer";
import ExploreOther from "@/app/components/ui/exploreOther";
-import BackToTopButton from "@/app/components/ui/backtotop";
+import VisualizerPageLayout, {
+ createVisualizerPaths,
+} from "@/app/visualizer/components/VisualizerPageLayout";
+import { MODULE_MAPS } from "@/lib/modulesMap";
export const metadata = {
title:
@@ -43,64 +42,26 @@ export const metadata = {
};
export default function Page() {
- const paths = [
- { name: "Home", href: "/" },
- { name: "Visualizer", href: "/visualizer" },
- { name: "Stack : Implementation Using Linked List", href: "" },
- ];
-
return (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
-
- Implementation Using Linked List
-
-
-
-
-
-
-
-
-
-
-
-
- }
+ content={ }
+ code={}
+ moduleCard={
+
+ }
+ exploreOther={
+
-
-
-
-
-
- >
+ }
+ />
);
}
diff --git a/app/visualizer/stack/isempty/page.jsx b/app/visualizer/stack/isempty/page.jsx
index e09765ce9..76e719f51 100755
--- a/app/visualizer/stack/isempty/page.jsx
+++ b/app/visualizer/stack/isempty/page.jsx
@@ -1,15 +1,14 @@
import Animation from "@/app/visualizer/stack/isempty/animation";
-import Navbar from "@/app/components/navbarinner";
-import Breadcrumbs from "@/app/components/ui/Breadcrumbs";
import ArticleActions from "@/app/components/ui/ArticleActions";
import Content from "@/app/visualizer/stack/isempty/content";
import Quiz from "@/app/visualizer/stack/isempty/quiz";
import Code from "@/app/visualizer/stack/isempty/codeBlock";
import ModuleCard from "@/app/components/ui/ModuleCard";
-import { MODULE_MAPS } from "@/lib/modulesMap";
import ExploreOther from "@/app/components/ui/exploreOther";
-import Footer from "@/app/components/footer";
-import BackToTopButton from "@/app/components/ui/backtotop";
+import VisualizerPageLayout, {
+ createVisualizerPaths,
+} from "@/app/visualizer/components/VisualizerPageLayout";
+import { MODULE_MAPS } from "@/lib/modulesMap";
export const metadata = {
title:
@@ -44,76 +43,32 @@ export const metadata = {
};
export default function Page() {
- const paths = [
- { name: "Home", href: "/" },
- { name: "Visualizer", href: "/visualizer" },
- { name: "Stack : IsEmpty", href: "" },
- ];
-
return (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
- IsEmpty Operation
-
-
-
-
-
-
-
-
-
-
-
- Test Your Knowledge before moving forward!
-
-
-
-
-
-
-
-
-
-
-
-
-
- >
+
}
+ animation={
}
+ content={
}
+ code={
}
+ quiz={
}
+ moduleCard={
+
+ }
+ exploreOther={
+
+ }
+ />
);
}
diff --git a/app/visualizer/stack/isfull/page.jsx b/app/visualizer/stack/isfull/page.jsx
index adb1a6960..c5aa52bda 100755
--- a/app/visualizer/stack/isfull/page.jsx
+++ b/app/visualizer/stack/isfull/page.jsx
@@ -1,15 +1,14 @@
import Animation from "@/app/visualizer/stack/isfull/animation";
-import Navbar from "@/app/components/navbarinner";
-import Breadcrumbs from "@/app/components/ui/Breadcrumbs";
import ArticleActions from "@/app/components/ui/ArticleActions";
import Content from "@/app/visualizer/stack/isfull/content";
import Quiz from "@/app/visualizer/stack/isfull/quiz";
import Code from "@/app/visualizer/stack/isfull/codeBlock";
import ModuleCard from "@/app/components/ui/ModuleCard";
+import ExploreOther from "@/app/components/ui/exploreOther";
+import VisualizerPageLayout, {
+ createVisualizerPaths,
+} from "@/app/visualizer/components/VisualizerPageLayout";
import { MODULE_MAPS } from "@/lib/modulesMap";
-import ExploreOther from '@/app/components/ui/exploreOther';
-import Footer from "@/app/components/footer";
-import BackToTopButton from "@/app/components/ui/backtotop";
export const metadata = {
title:
@@ -44,76 +43,32 @@ export const metadata = {
};
export default function Page() {
- const paths = [
- { name: "Home", href: "/" },
- { name: "Visualizer", href: "/visualizer" },
- { name: "Stack : IsFull", href: "" },
- ];
-
return (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
- IsFull Operation
-
-
-
-
-
-
-
-
-
-
-
- Test Your Knowledge before moving forward!
-
-
-
-
-
-
-
-
-
-
-
-
-
- >
+
}
+ animation={
}
+ content={
}
+ code={
}
+ quiz={
}
+ moduleCard={
+
+ }
+ exploreOther={
+
+ }
+ />
);
}
diff --git a/app/visualizer/stack/peek/page.jsx b/app/visualizer/stack/peek/page.jsx
index 5a865d8ff..1f43db06c 100755
--- a/app/visualizer/stack/peek/page.jsx
+++ b/app/visualizer/stack/peek/page.jsx
@@ -1,15 +1,14 @@
import Animation from "@/app/visualizer/stack/peek/animation";
-import Navbar from "@/app/components/navbarinner";
-import Breadcrumbs from "@/app/components/ui/Breadcrumbs";
import ArticleActions from "@/app/components/ui/ArticleActions";
import Content from "@/app/visualizer/stack/peek/content";
import Quiz from "@/app/visualizer/stack/peek/quiz";
import Code from "@/app/visualizer/stack/peek/codeBlock";
import ModuleCard from "@/app/components/ui/ModuleCard";
+import ExploreOther from "@/app/components/ui/exploreOther";
+import VisualizerPageLayout, {
+ createVisualizerPaths,
+} from "@/app/visualizer/components/VisualizerPageLayout";
import { MODULE_MAPS } from "@/lib/modulesMap";
-import ExploreOther from '@/app/components/ui/exploreOther';
-import Footer from '@/app/components/footer';
-import BackToTopButton from '@/app/components/ui/backtotop';
export const metadata = {
title:
@@ -45,64 +44,24 @@ export const metadata = {
};
export default function Page() {
- const paths = [
- { name: "Home", href: "/" },
- { name: "Visualizer", href: "/visualizer" },
- { name: "Stack : Peek", href: "" },
- ];
-
return (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
- Peek Operation
-
-
-
-
-
-
-
-
-
-
-
- Test Your Knowledge before moving forward!
-
-
-
-
-
-
-
-
-
- }
+ animation={ }
+ content={ }
+ code={}
+ quiz={ }
+ moduleCard={
+
+ }
+ exploreOther={
+
-
-
-
-
-
- >
+ }
+ />
);
}
diff --git a/app/visualizer/stack/polish/postfix/page.jsx b/app/visualizer/stack/polish/postfix/page.jsx
index 5ce7e3aec..59d50062e 100755
--- a/app/visualizer/stack/polish/postfix/page.jsx
+++ b/app/visualizer/stack/polish/postfix/page.jsx
@@ -1,15 +1,14 @@
import Animation from "@/app/visualizer/stack/polish/postfix/animation";
-import Navbar from "@/app/components/navbarinner";
-import Breadcrumbs from "@/app/components/ui/Breadcrumbs";
import ArticleActions from "@/app/components/ui/ArticleActions";
import Content from "@/app/visualizer/stack/polish/postfix/content";
import Quiz from "@/app/visualizer/stack/polish/postfix/quiz";
import Code from "@/app/visualizer/stack/polish/postfix/codeBlock";
import ModuleCard from "@/app/components/ui/ModuleCard";
-import { MODULE_MAPS } from "@/lib/modulesMap";
-import Footer from "@/app/components/footer";
import ExploreOther from "@/app/components/ui/exploreOther";
-import BackToTopButton from "@/app/components/ui/backtotop";
+import VisualizerPageLayout, {
+ createVisualizerPaths,
+} from "@/app/visualizer/components/VisualizerPageLayout";
+import { MODULE_MAPS } from "@/lib/modulesMap";
export const metadata = {
title:
@@ -45,73 +44,28 @@ export const metadata = {
};
export default function Page() {
- const paths = [
- { name: "Home", href: "/" },
- { name: "Visualizer", href: "/visualizer" },
- { name: "Stack : Infix to postfix", href: "" },
- ];
-
return (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
-
- Infix to Postfix
-
-
-
-
-
-
-
-
-
-
-
- Test Your Knowledge before moving forward!
-
-
-
-
-
-
-
-
-
-
-
-
-
- >
+
}
+ animation={
}
+ content={
}
+ code={
}
+ quiz={
}
+ moduleCard={
+
+ }
+ exploreOther={
+
+ }
+ />
);
}
diff --git a/app/visualizer/stack/polish/prefix/page.jsx b/app/visualizer/stack/polish/prefix/page.jsx
index aec445bd5..62b11c527 100755
--- a/app/visualizer/stack/polish/prefix/page.jsx
+++ b/app/visualizer/stack/polish/prefix/page.jsx
@@ -1,15 +1,14 @@
import Animation from "@/app/visualizer/stack/polish/prefix/animation";
-import Navbar from "@/app/components/navbarinner";
-import Breadcrumbs from "@/app/components/ui/Breadcrumbs";
import ArticleActions from "@/app/components/ui/ArticleActions";
import Content from "@/app/visualizer/stack/polish/prefix/content";
import Quiz from "@/app/visualizer/stack/polish/postfix/quiz";
import Code from "@/app/visualizer/stack/polish/prefix/codeBlock";
import ModuleCard from "@/app/components/ui/ModuleCard";
-import { MODULE_MAPS } from "@/lib/modulesMap";
-import Footer from "@/app/components/footer";
import ExploreOther from "@/app/components/ui/exploreOther";
-import BackToTopButton from "@/app/components/ui/backtotop";
+import VisualizerPageLayout, {
+ createVisualizerPaths,
+} from "@/app/visualizer/components/VisualizerPageLayout";
+import { MODULE_MAPS } from "@/lib/modulesMap";
export const metadata = {
title:
@@ -45,75 +44,28 @@ export const metadata = {
};
export default function Page() {
- const paths = [
- { name: "Home", href: "/" },
- { name: "Visualizer", href: "/visualizer" },
- { name: "Stack : Infix to prefix", href: "" },
- ];
-
return (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
-
- Infix to Prefix
-
-
-
-
-
-
-
-
-
-
-
- Test Your Knowledge before moving forward!
-
-
-
-
-
-
-
-
-
- }
+ animation={ }
+ content={ }
+ code={}
+ quiz={ }
+ moduleCard={
+
+ }
+ exploreOther={
+
-
-
-
-
-
- >
+ }
+ />
);
}
diff --git a/app/visualizer/stack/push-pop/page.jsx b/app/visualizer/stack/push-pop/page.jsx
index 1ca1b3c14..ceccdc390 100755
--- a/app/visualizer/stack/push-pop/page.jsx
+++ b/app/visualizer/stack/push-pop/page.jsx
@@ -1,15 +1,14 @@
import Animation from "@/app/visualizer/stack/push-pop/animation";
-import Navbar from "@/app/components/navbarinner";
-import Breadcrumbs from "@/app/components/ui/Breadcrumbs";
import ArticleActions from "@/app/components/ui/ArticleActions";
import Content from "@/app/visualizer/stack/push-pop/content";
import Quiz from "@/app/visualizer/stack/push-pop/quiz";
import Code from "@/app/visualizer/stack/push-pop/codeBlock";
import ExploreOther from "@/app/components/ui/exploreOther";
import ModuleCard from "@/app/components/ui/ModuleCard";
+import VisualizerPageLayout, {
+ createVisualizerPaths,
+} from "@/app/visualizer/components/VisualizerPageLayout";
import { MODULE_MAPS } from "@/lib/modulesMap";
-import Footer from '@/app/components/footer';
-import BackToTopButton from '@/app/components/ui/backtotop';
export const metadata = {
title:
@@ -49,76 +48,32 @@ export const metadata = {
};
export default function Page() {
- const paths = [
- { name: "Home", href: "/" },
- { name: "Visualizer", href: "/visualizer" },
- { name: "Stack : Push & Pop", href: "" },
- ];
-
return (
- <>
-
-
-
-
-
-
-
-
-
-
-
- Test Your Knowledge before moving forward!
-
-
-
-
-
-
-
-
-
-
-
-
-
- >
+
}
+ animation={
}
+ content={
}
+ code={
}
+ quiz={
}
+ moduleCard={
+
+ }
+ exploreOther={
+
+ }
+ />
);
}
diff --git a/app/visualizer/stack/push-pop/quiz.jsx b/app/visualizer/stack/push-pop/quiz.jsx
index eaefc3f9a..bbb5f7271 100755
--- a/app/visualizer/stack/push-pop/quiz.jsx
+++ b/app/visualizer/stack/push-pop/quiz.jsx
@@ -173,30 +173,30 @@ const StackQuiz = () => {
const calculateWeakAreas = () => {
const weakAreas = [];
if (answers[0] !== questions[0].correctAnswer) {
- weakAreas.push("understanding the basic principle of Selection Sort");
+ weakAreas.push("understanding the LIFO principle of stack");
}
if (answers[1] !== questions[1].correctAnswer) {
- weakAreas.push("time complexity analysis");
+ weakAreas.push("stack operation time complexity");
}
if (answers[2] !== questions[2].correctAnswer) {
- weakAreas.push("counting swaps in Selection Sort");
+ weakAreas.push("stack overflow and underflow conditions");
}
if (answers[3] !== questions[3].correctAnswer) {
- weakAreas.push("comparison with other simple sorts");
+ weakAreas.push("comparison of stack with other data structures");
}
if (answers[4] !== questions[4].correctAnswer) {
- weakAreas.push("space complexity");
+ weakAreas.push("space complexity of stack");
}
if (answers[5] !== questions[5].correctAnswer) {
- weakAreas.push("stability characteristics");
+ weakAreas.push("stack memory and call stack behavior");
}
if (answers[6] !== questions[6].correctAnswer) {
- weakAreas.push("practical applications");
+ weakAreas.push("stack application such as expression and undo operations");
}
return weakAreas.length > 0
? `Focus on improving: ${weakAreas.join(', ')}. Review the corresponding sections above.`
- : "Perfect! You've mastered all Selection Sort concepts!";
+ : "Perfect! You've mastered all Stack concepts!";
};
const startQuiz = () => {
diff --git a/app/visualizer/trees/binaryTree/types/content.jsx b/app/visualizer/trees/binaryTree/types/content.jsx
index e24da0d04..0884cffaa 100755
--- a/app/visualizer/trees/binaryTree/types/content.jsx
+++ b/app/visualizer/trees/binaryTree/types/content.jsx
@@ -1,3 +1,5 @@
+"use client";
+
/* content.jsx */
import React, { useEffect, useRef } from 'react';
import { gsap } from 'gsap';
@@ -295,4 +297,4 @@ export default function content() {
);
-}
\ No newline at end of file
+}
diff --git a/app/visualizer/trees/binaryTree/types/page.jsx b/app/visualizer/trees/binaryTree/types/page.jsx
index a12c7cdb4..dd33e53da 100755
--- a/app/visualizer/trees/binaryTree/types/page.jsx
+++ b/app/visualizer/trees/binaryTree/types/page.jsx
@@ -1,33 +1,46 @@
-import Animation from "@/app/visualizer/trees/binaryTree/types/animation";
-import Navbar from "@/app/components/navbarinner";
+import Content from "@/app/visualizer/trees/binaryTree/types/content";
+import CodeBlock from "@/app/visualizer/trees/binaryTree/types/codeBlock";
+import ExploreOther from "@/app/components/ui/exploreOther";
+import VisualizerPageLayout, {
+ createVisualizerPaths,
+} from "@/app/visualizer/components/VisualizerPageLayout";
export const metadata = {
- title: 'Binary Tree Types | Learn Full, Complete, and Degenerate Binary Trees in DSA',
- description: 'Learn about Binary Tree types in Data Structures and Algorithms, including Full Binary Tree, Complete Binary Tree, and Degenerate Tree with clear visual explanations, animations, and code examples in JavaScript, C, Python, and Java.',
+ title: "Binary Tree Types | Learn Full, Complete, and Degenerate Binary Trees in DSA",
+ description:
+ "Learn about Binary Tree types in Data Structures and Algorithms, including Full Binary Tree, Complete Binary Tree, and Degenerate Tree with clear visual explanations, animations, and code examples in JavaScript, C, Python, and Java.",
keywords: [
- 'Binary Tree',
- 'Binary Tree Types',
- 'Full Binary Tree',
- 'Complete Binary Tree',
- 'Degenerate Tree',
- 'Binary Tree Visualization',
- 'DSA Binary Trees',
- 'Binary Tree Animation',
- 'Binary Tree Implementation',
- 'Binary Tree in JavaScript',
- 'Binary Tree in C',
- 'Binary Tree in Python',
- 'Binary Tree in Java',
- 'Learn Binary Trees DSA',
+ "Binary Tree",
+ "Binary Tree Types",
+ "Full Binary Tree",
+ "Complete Binary Tree",
+ "Degenerate Tree",
+ "Binary Tree Visualization",
+ "DSA Binary Trees",
+ "Binary Tree Animation",
+ "Binary Tree Implementation",
+ "Binary Tree in JavaScript",
+ "Binary Tree in C",
+ "Binary Tree in Python",
+ "Binary Tree in Java",
+ "Learn Binary Trees DSA",
],
- robots: 'index, follow',
+ robots: "index, follow",
};
-export default function Page(){
- return(
- <>
-
-
- >
+export default function Page() {
+ return (
+
}
+ code={
}
+ exploreOther={
+
+ }
+ />
);
-};
\ No newline at end of file
+}
diff --git a/app/visualizer/trees/traversing/TreeTraversalVisualizer.jsx b/app/visualizer/trees/traversing/TreeTraversalVisualizer.jsx
new file mode 100644
index 000000000..f9509293d
--- /dev/null
+++ b/app/visualizer/trees/traversing/TreeTraversalVisualizer.jsx
@@ -0,0 +1,1662 @@
+'use client';
+import React, { useState, useRef, useEffect } from 'react';
+import Navbar from '@/app/components/navbarinner';
+import Footer from '@/app/components/footer';
+import { Play, Pause, ChevronRight, ChevronLeft, RotateCcw, AlertCircle, CheckCircle, Info, BookOpen, Layers, Award } from 'lucide-react';
+
+class TreeNode {
+ constructor(value) {
+ this.value = value;
+ this.left = null;
+ this.right = null;
+ }
+}
+
+// Complexity mapping
+const complexityInfo = {
+ 'pre-order': {
+ time: 'O(N)',
+ timeDesc: 'Every node in the tree is visited exactly once.',
+ space: 'O(H)',
+ spaceDesc: 'H is the height of the tree. In the worst case (skewed tree), the call stack requires O(N) space, while in a balanced tree it takes O(log N).'
+ },
+ 'in-order': {
+ time: 'O(N)',
+ timeDesc: 'Every node in the tree is visited exactly once.',
+ space: 'O(H)',
+ spaceDesc: 'H is the height of the tree. The call stack holds function calls up to the deepest leaf node.'
+ },
+ 'post-order': {
+ time: 'O(N)',
+ timeDesc: 'Every node in the tree is visited exactly once.',
+ space: 'O(H)',
+ spaceDesc: 'H is the height of the tree. Space is required for recursion stack frames representing parents waiting to be visited.'
+ },
+ 'level-order': {
+ time: 'O(N)',
+ timeDesc: 'Every node is dequeued and visited once.',
+ space: 'O(W)',
+ spaceDesc: 'W is the maximum width of the tree. In a complete binary tree, the last level contains N/2 nodes, requiring O(N) space in the queue.'
+ },
+ 'morris': {
+ time: 'O(N)',
+ timeDesc: 'Each edge is traversed at most 3 times (finding predecessor, creating thread, removing thread). Thus, overall time remains O(N).',
+ space: 'O(1)',
+ spaceDesc: 'Extraordinary feature: Morris Traversal uses threaded binary trees to navigate without recursion stack or queues, achieving true constant auxiliary space!'
+ }
+};
+
+// Pseudocode mapping
+const pseudocode = {
+ 'pre-order': [
+ 'preOrder(node) {',
+ ' if (node === null) return;',
+ ' visit(node); // Process current node',
+ ' preOrder(node.left); // Traverse left subtree',
+ ' preOrder(node.right); // Traverse right subtree',
+ '}'
+ ],
+ 'in-order': [
+ 'inOrder(node) {',
+ ' if (node === null) return;',
+ ' inOrder(node.left); // Traverse left subtree',
+ ' visit(node); // Process current node',
+ ' inOrder(node.right); // Traverse right subtree',
+ '}'
+ ],
+ 'post-order': [
+ 'postOrder(node) {',
+ ' if (node === null) return;',
+ ' postOrder(node.left); // Traverse left subtree',
+ ' postOrder(node.right); // Traverse right subtree',
+ ' visit(node); // Process current node',
+ '}'
+ ],
+ 'level-order': [
+ 'levelOrder(root) {',
+ ' if (root === null) return;',
+ ' queue.enqueue(root);',
+ ' while (!queue.isEmpty()) {',
+ ' node = queue.dequeue(); visit(node);',
+ ' if (node.left) queue.enqueue(node.left);',
+ ' if (node.right) queue.enqueue(node.right);',
+ ' }',
+ '}'
+ ],
+ 'morris': [
+ 'morrisInOrder(root) {',
+ ' curr = root;',
+ ' while (curr !== null) {',
+ ' if (curr.left === null) {',
+ ' visit(curr); curr = curr.right;',
+ ' } else {',
+ ' pred = findPredecessor(curr);',
+ ' if (pred.right === null) {',
+ ' pred.right = curr; // Create thread',
+ ' curr = curr.left;',
+ ' } else {',
+ ' pred.right = null; // Clear thread',
+ ' visit(curr); curr = curr.right;',
+ ' }',
+ ' }',
+ ' }',
+ '}'
+ ]
+};
+
+// Quizzes data
+const quizzes = {
+ 'pre-order': [
+ {
+ question: 'Which of the following describes the visiting order of Pre-Order traversal?',
+ options: [
+ 'Root, Left Subtree, Right Subtree',
+ 'Left Subtree, Root, Right Subtree',
+ 'Left Subtree, Right Subtree, Root',
+ 'Root, Right Subtree, Left Subtree'
+ ],
+ answer: 0,
+ explanation: 'Pre-Order traversal is so named because the Root node is visited PRE (before) its subtrees. So the order is Root -> Left -> Right.'
+ },
+ {
+ question: 'Which of the following is a common application of Pre-Order traversal?',
+ options: [
+ 'Getting sorted nodes of a BST',
+ 'Creating a copy or clone of a binary tree',
+ 'Deleting/destroying a tree to avoid memory leaks',
+ 'Finding the height of a tree'
+ ],
+ answer: 1,
+ explanation: 'Pre-Order traversal is commonly used to create a copy or serialize a binary tree, because the root is created first, followed by its children recursively.'
+ }
+ ],
+ 'in-order': [
+ {
+ question: 'What is the special characteristic of In-Order traversal on a Binary Search Tree (BST)?',
+ options: [
+ 'It visits the nodes in random order',
+ 'It visits the nodes in sorted ascending order',
+ 'It visits the nodes in sorted descending order',
+ 'It visits leaves first, then intermediate parent nodes'
+ ],
+ answer: 1,
+ explanation: 'Because of the BST property (Left < Root < Right), an In-Order traversal (Left -> Root -> Right) always produces the values in sorted ascending order.'
+ },
+ {
+ question: 'What is the auxiliary space complexity of standard In-Order traversal on a balanced binary tree?',
+ options: [
+ 'O(1)',
+ 'O(log N) due to recursion stack height',
+ 'O(N) always',
+ 'O(N^2)'
+ ],
+ answer: 1,
+ explanation: 'In a balanced binary tree, the height of the tree is log(N). Since the recursion call stack goes as deep as the height, the auxiliary space is O(log N).'
+ }
+ ],
+ 'post-order': [
+ {
+ question: 'What is the visiting order of Post-Order traversal?',
+ options: [
+ 'Root, Left Subtree, Right Subtree',
+ 'Left Subtree, Root, Right Subtree',
+ 'Left Subtree, Right Subtree, Root',
+ 'Root, Right Subtree, Left Subtree'
+ ],
+ answer: 2,
+ explanation: 'Post-Order traversal visits the Root node POST (after) its subtrees. So the order is Left -> Right -> Root.'
+ },
+ {
+ question: 'Why is Post-Order traversal ideal for deleting or freeing memory of a binary tree?',
+ options: [
+ 'It is faster than other traversals',
+ 'It processes the root before any of its descendants',
+ 'It processes children first, ensuring parents are deleted only after children are safe to delete',
+ 'It uses zero auxiliary memory space'
+ ],
+ answer: 2,
+ explanation: 'Post-Order processes children before parent (Left -> Right -> Root). This ensures you delete child nodes first so you do not lose references to children before deleting their parent!'
+ }
+ ],
+ 'level-order': [
+ {
+ question: 'What data structure is standard for implementing Level-Order traversal iteratively?',
+ options: [
+ 'Stack (LIFO)',
+ 'Queue (FIFO)',
+ 'Priority Queue',
+ 'Hash Map'
+ ],
+ answer: 1,
+ explanation: 'A Queue (First-In-First-Out) is used to explore nodes level-by-level, ensuring nodes at level L are processed before any nodes at level L+1.'
+ },
+ {
+ question: 'In the worst case (e.g. a complete binary tree), what is the maximum number of nodes inside the queue at any time?',
+ options: [
+ 'O(1)',
+ 'O(log N)',
+ 'O(N) (specifically, about N/2 at the leaf level)',
+ 'O(N log N)'
+ ],
+ answer: 2,
+ explanation: 'In a complete binary tree, the leaf level contains roughly N/2 nodes. In Level-Order traversal, the queue will hold all nodes of the leaf level at the same time, making space complexity O(N).'
+ }
+ ],
+ 'morris': [
+ {
+ question: 'What is the main advantage of Morris Traversal over standard traversals?',
+ options: [
+ 'It runs in O(log N) time complexity',
+ 'It performs the traversal with O(1) auxiliary space, requiring no recursion stack or queue',
+ 'It works on graphs without modification',
+ 'It avoids visiting left subtrees altogether'
+ ],
+ answer: 1,
+ explanation: 'Morris Traversal uses the concept of threaded binary trees (temporary links from inorder predecessors back to current nodes) to traverse the tree without any recursion stack or auxiliary queues, achieving true O(1) space!'
+ },
+ {
+ question: 'What does Morris Traversal do to a node\'s inorder predecessor when visiting for the first time?',
+ options: [
+ 'Deletes the predecessor node',
+ 'Swaps it with the root node',
+ 'Sets its right child to point to the current node (creating a temporary thread)',
+ 'Sets its left child to point to the current node'
+ ],
+ answer: 2,
+ explanation: 'Morris Traversal finds the inorder predecessor (rightmost node in left subtree) and sets its right child to point back to the current node. This thread is later removed when traversing that path a second time.'
+ }
+ ]
+};
+
+export default function TreeTraversalVisualizer({ initialMode = 'in-order' }) {
+ const [mode, setMode] = useState(initialMode);
+ const [root, setRoot] = useState(null);
+ const [inputValue, setInputValue] = useState('');
+ const [message, setMessage] = useState('Tree is empty');
+
+ // Traversal playback states
+ const [steps, setSteps] = useState([]);
+ const [currentStepIdx, setCurrentStepIdx] = useState(-1);
+ const [isAnimating, setIsAnimating] = useState(false);
+ const [speed, setSpeed] = useState(1);
+
+ // Quiz states
+ const [quizIdx, setQuizIdx] = useState(0);
+ const [selectedOption, setSelectedOption] = useState(null);
+ const [quizSubmitted, setQuizSubmitted] = useState(false);
+
+ const timerRef = useRef(null);
+
+ // Sync mode changes
+ useEffect(() => {
+ setMode(initialMode);
+ resetPlayback();
+ }, [initialMode]);
+
+ // Pure functional BST insertion
+ const insertNode = (node, value) => {
+ if (!node) return new TreeNode(value);
+ if (value < node.value) {
+ return { ...node, left: insertNode(node.left, value) };
+ } else if (value > node.value) {
+ return { ...node, right: insertNode(node.right, value) };
+ }
+ return node; // No duplicates allowed for visual clarity
+ };
+
+ const handleInsert = () => {
+ const value = parseInt(inputValue);
+ if (isNaN(value)) {
+ setMessage('⚠️ Please enter a valid number');
+ return;
+ }
+ if (value < 1 || value > 999) {
+ setMessage('⚠️ Please enter a number between 1 and 999');
+ return;
+ }
+
+ setRoot(prev => {
+ const newRoot = insertNode(prev, value);
+ setMessage(`Inserted node ${value}`);
+ return newRoot;
+ });
+ setInputValue('');
+ resetPlayback();
+ };
+
+ // Generate random BST
+ const generateRandomTree = () => {
+ resetPlayback();
+ // Predefined sequences that create highly balanced, beautiful trees
+ const trees = [
+ [50, 30, 70, 20, 40, 60, 80],
+ [45, 25, 65, 15, 35, 55, 75],
+ [55, 35, 75, 25, 45, 65, 85, 15, 95],
+ [50, 25, 75, 12, 37, 62, 87],
+ [40, 20, 60, 10, 30, 50, 70, 5, 15, 25]
+ ];
+ const sequence = trees[Math.floor(Math.random() * trees.length)];
+
+ let newRoot = null;
+ sequence.forEach(val => {
+ newRoot = insertNode(newRoot, val);
+ });
+
+ setRoot(newRoot);
+ setMessage(`Generated beautiful BST with ${sequence.length} nodes.`);
+ };
+
+ // Load a default tree on mount if empty
+ useEffect(() => {
+ if (!root) {
+ generateRandomTree();
+ }
+ }, []);
+
+ // Pre-calculate steps based on current tree and traversal mode
+ const preCalculateSteps = () => {
+ if (!root) return [];
+
+ switch (mode) {
+ case 'pre-order':
+ return generatePreOrderSteps(root);
+ case 'in-order':
+ return generateInOrderSteps(root);
+ case 'post-order':
+ return generatePostOrderSteps(root);
+ case 'level-order':
+ return generateLevelOrderSteps(root);
+ case 'morris':
+ return generateMorrisSteps(root);
+ default:
+ return [];
+ }
+ };
+
+ // Traversal path step generators
+ const generatePreOrderSteps = (treeRoot) => {
+ const records = [];
+ const visited = [];
+ const stack = [];
+
+ const traverse = (node) => {
+ if (!node) return;
+
+ // Line 2: visit
+ visited.push(node.value);
+ records.push({
+ currentNode: node.value,
+ visited: [...visited],
+ explanation: `Visit node ${node.value} and add it to our traversal result list.`,
+ codeLine: 2,
+ stack: [...stack].map(n => n.value),
+ highlightedNodes: { [node.value]: 'visiting' },
+ threads: []
+ });
+
+ // Line 3: left child
+ if (node.left) {
+ stack.push(node);
+ records.push({
+ currentNode: node.left.value,
+ visited: [...visited],
+ explanation: `Move to the left child of ${node.value} -> ${node.left.value}.`,
+ codeLine: 3,
+ stack: [...stack].map(n => n.value),
+ highlightedNodes: { [node.value]: 'active', [node.left.value]: 'visiting' },
+ threads: []
+ });
+ traverse(node.left);
+ stack.pop();
+ } else {
+ records.push({
+ currentNode: node.value,
+ visited: [...visited],
+ explanation: `Node ${node.value} has no left child. So we skip left subtree recursion.`,
+ codeLine: 3,
+ stack: [...stack].map(n => n.value),
+ highlightedNodes: { [node.value]: 'active' },
+ threads: []
+ });
+ }
+
+ // Line 4: right child
+ if (node.right) {
+ stack.push(node);
+ records.push({
+ currentNode: node.right.value,
+ visited: [...visited],
+ explanation: `Move to the right child of ${node.value} -> ${node.right.value}.`,
+ codeLine: 4,
+ stack: [...stack].map(n => n.value),
+ highlightedNodes: { [node.value]: 'active', [node.right.value]: 'visiting' },
+ threads: []
+ });
+ traverse(node.right);
+ stack.pop();
+ } else {
+ records.push({
+ currentNode: node.value,
+ visited: [...visited],
+ explanation: `Node ${node.value} has no right child. So we skip right subtree recursion.`,
+ codeLine: 4,
+ stack: [...stack].map(n => n.value),
+ highlightedNodes: { [node.value]: 'active' },
+ threads: []
+ });
+ }
+ };
+
+ records.push({
+ currentNode: treeRoot.value,
+ visited: [],
+ explanation: `Start Pre-Order traversal from the root node ${treeRoot.value}.`,
+ codeLine: 0,
+ stack: [],
+ highlightedNodes: {},
+ threads: []
+ });
+
+ traverse(treeRoot);
+
+ records.push({
+ currentNode: null,
+ visited: [...visited],
+ explanation: `Pre-Order traversal is complete! Visited nodes: [${visited.join(', ')}].`,
+ codeLine: 5,
+ stack: [],
+ highlightedNodes: {},
+ threads: []
+ });
+
+ return records;
+ };
+
+ const generateInOrderSteps = (treeRoot) => {
+ const records = [];
+ const visited = [];
+ const stack = [];
+
+ const traverse = (node) => {
+ if (!node) return;
+
+ // Line 2: left subtree
+ if (node.left) {
+ stack.push(node);
+ records.push({
+ currentNode: node.left.value,
+ visited: [...visited],
+ explanation: `Recurse into left child of ${node.value} -> ${node.left.value}.`,
+ codeLine: 2,
+ stack: [...stack].map(n => n.value),
+ highlightedNodes: { [node.value]: 'active', [node.left.value]: 'visiting' },
+ threads: []
+ });
+ traverse(node.left);
+ stack.pop();
+ } else {
+ records.push({
+ currentNode: node.value,
+ visited: [...visited],
+ explanation: `Node ${node.value} has no left child. Backtracking...`,
+ codeLine: 2,
+ stack: [...stack].map(n => n.value),
+ highlightedNodes: { [node.value]: 'active' },
+ threads: []
+ });
+ }
+
+ // Line 3: visit
+ visited.push(node.value);
+ records.push({
+ currentNode: node.value,
+ visited: [...visited],
+ explanation: `Visit node ${node.value} and add it to our traversal path.`,
+ codeLine: 3,
+ stack: [...stack].map(n => n.value),
+ highlightedNodes: { [node.value]: 'visiting' },
+ threads: []
+ });
+
+ // Line 4: right subtree
+ if (node.right) {
+ stack.push(node);
+ records.push({
+ currentNode: node.right.value,
+ visited: [...visited],
+ explanation: `Recurse into right child of ${node.value} -> ${node.right.value}.`,
+ codeLine: 4,
+ stack: [...stack].map(n => n.value),
+ highlightedNodes: { [node.value]: 'active', [node.right.value]: 'visiting' },
+ threads: []
+ });
+ traverse(node.right);
+ stack.pop();
+ } else {
+ records.push({
+ currentNode: node.value,
+ visited: [...visited],
+ explanation: `Node ${node.value} has no right child. Backtracking...`,
+ codeLine: 4,
+ stack: [...stack].map(n => n.value),
+ highlightedNodes: { [node.value]: 'active' },
+ threads: []
+ });
+ }
+ };
+
+ records.push({
+ currentNode: treeRoot.value,
+ visited: [],
+ explanation: `Start In-Order traversal from the root node ${treeRoot.value}.`,
+ codeLine: 0,
+ stack: [],
+ highlightedNodes: {},
+ threads: []
+ });
+
+ traverse(treeRoot);
+
+ records.push({
+ currentNode: null,
+ visited: [...visited],
+ explanation: `In-Order traversal is complete! Notice that the BST values are printed in sorted order: [${visited.join(', ')}].`,
+ codeLine: 5,
+ stack: [],
+ highlightedNodes: {},
+ threads: []
+ });
+
+ return records;
+ };
+
+ const generatePostOrderSteps = (treeRoot) => {
+ const records = [];
+ const visited = [];
+ const stack = [];
+
+ const traverse = (node) => {
+ if (!node) return;
+
+ // Line 2: left subtree
+ if (node.left) {
+ stack.push(node);
+ records.push({
+ currentNode: node.left.value,
+ visited: [...visited],
+ explanation: `Recurse into left child of ${node.value} -> ${node.left.value}.`,
+ codeLine: 2,
+ stack: [...stack].map(n => n.value),
+ highlightedNodes: { [node.value]: 'active', [node.left.value]: 'visiting' },
+ threads: []
+ });
+ traverse(node.left);
+ stack.pop();
+ } else {
+ records.push({
+ currentNode: node.value,
+ visited: [...visited],
+ explanation: `Node ${node.value} has no left child.`,
+ codeLine: 2,
+ stack: [...stack].map(n => n.value),
+ highlightedNodes: { [node.value]: 'active' },
+ threads: []
+ });
+ }
+
+ // Line 3: right subtree
+ if (node.right) {
+ stack.push(node);
+ records.push({
+ currentNode: node.right.value,
+ visited: [...visited],
+ explanation: `Recurse into right child of ${node.value} -> ${node.right.value}.`,
+ codeLine: 3,
+ stack: [...stack].map(n => n.value),
+ highlightedNodes: { [node.value]: 'active', [node.right.value]: 'visiting' },
+ threads: []
+ });
+ traverse(node.right);
+ stack.pop();
+ } else {
+ records.push({
+ currentNode: node.value,
+ visited: [...visited],
+ explanation: `Node ${node.value} has no right child.`,
+ codeLine: 3,
+ stack: [...stack].map(n => n.value),
+ highlightedNodes: { [node.value]: 'active' },
+ threads: []
+ });
+ }
+
+ // Line 4: visit
+ visited.push(node.value);
+ records.push({
+ currentNode: node.value,
+ visited: [...visited],
+ explanation: `Both subtrees of node ${node.value} are fully processed. Now we visit the parent node itself.`,
+ codeLine: 4,
+ stack: [...stack].map(n => n.value),
+ highlightedNodes: { [node.value]: 'visiting' },
+ threads: []
+ });
+ };
+
+ records.push({
+ currentNode: treeRoot.value,
+ visited: [],
+ explanation: `Start Post-Order traversal from the root node ${treeRoot.value}.`,
+ codeLine: 0,
+ stack: [],
+ highlightedNodes: {},
+ threads: []
+ });
+
+ traverse(treeRoot);
+
+ records.push({
+ currentNode: null,
+ visited: [...visited],
+ explanation: `Post-Order traversal is complete! Visited nodes: [${visited.join(', ')}].`,
+ codeLine: 5,
+ stack: [],
+ highlightedNodes: {},
+ threads: []
+ });
+
+ return records;
+ };
+
+ const generateLevelOrderSteps = (treeRoot) => {
+ const records = [];
+ const visited = [];
+ const queue = [];
+
+ records.push({
+ currentNode: treeRoot.value,
+ visited: [],
+ queue: [treeRoot.value],
+ explanation: `Start Level-Order traversal. Enqueue root node ${treeRoot.value}.`,
+ codeLine: 2,
+ stack: [],
+ highlightedNodes: { [treeRoot.value]: 'visiting' },
+ threads: []
+ });
+
+ const runQueue = [{ node: treeRoot, path: [treeRoot.value] }];
+
+ while (runQueue.length > 0) {
+ const currentItem = runQueue.shift();
+ const node = currentItem.node;
+
+ visited.push(node.value);
+
+ const currentQueueValues = runQueue.map(q => q.node.value);
+
+ // Line 4: Dequeue and visit
+ records.push({
+ currentNode: node.value,
+ visited: [...visited],
+ queue: [node.value, ...currentQueueValues],
+ explanation: `Dequeue node ${node.value} from the queue and visit it. Current path: [${visited.join(', ')}].`,
+ codeLine: 4,
+ stack: [],
+ highlightedNodes: { [node.value]: 'visiting' },
+ threads: []
+ });
+
+ // Line 5: Enqueue left child
+ if (node.left) {
+ currentQueueValues.push(node.left.value);
+ runQueue.push({ node: node.left, path: [...currentItem.path, node.left.value] });
+ records.push({
+ currentNode: node.value,
+ visited: [...visited],
+ queue: [...currentQueueValues],
+ explanation: `Node ${node.value} has a left child ${node.left.value}. Add it to the back of the queue.`,
+ codeLine: 5,
+ stack: [],
+ highlightedNodes: { [node.value]: 'active', [node.left.value]: 'visiting' },
+ threads: []
+ });
+ } else {
+ records.push({
+ currentNode: node.value,
+ visited: [...visited],
+ queue: [...currentQueueValues],
+ explanation: `Node ${node.value} has no left child. Nothing to enqueue.`,
+ codeLine: 5,
+ stack: [],
+ highlightedNodes: { [node.value]: 'active' },
+ threads: []
+ });
+ }
+
+ // Line 6: Enqueue right child
+ if (node.right) {
+ currentQueueValues.push(node.right.value);
+ runQueue.push({ node: node.right, path: [...currentItem.path, node.right.value] });
+ records.push({
+ currentNode: node.value,
+ visited: [...visited],
+ queue: [...currentQueueValues],
+ explanation: `Node ${node.value} has a right child ${node.right.value}. Add it to the back of the queue.`,
+ codeLine: 6,
+ stack: [],
+ highlightedNodes: { [node.value]: 'active', [node.right.value]: 'visiting' },
+ threads: []
+ });
+ } else {
+ records.push({
+ currentNode: node.value,
+ visited: [...visited],
+ queue: [...currentQueueValues],
+ explanation: `Node ${node.value} has no right child. Nothing to enqueue.`,
+ codeLine: 6,
+ stack: [],
+ highlightedNodes: { [node.value]: 'active' },
+ threads: []
+ });
+ }
+ }
+
+ records.push({
+ currentNode: null,
+ visited: [...visited],
+ queue: [],
+ explanation: `The queue is empty! Level-Order traversal successfully finished.`,
+ codeLine: 7,
+ stack: [],
+ highlightedNodes: {},
+ threads: []
+ });
+
+ return records;
+ };
+
+ const generateMorrisSteps = (treeRoot) => {
+ const records = [];
+ const visited = [];
+ let activeThreads = [];
+
+ // Predecessor finder helper
+ const findPredecessor = (currNode, leftSubtree) => {
+ let pNode = leftSubtree;
+ while (pNode.right && pNode.right.value !== currNode.value) {
+ pNode = pNode.right;
+ }
+ return pNode;
+ };
+
+ let curr = treeRoot;
+
+ records.push({
+ currentNode: curr.value,
+ visited: [],
+ threads: [],
+ explanation: `Initialize Morris Traversal. Set curr = root node (${curr.value}).`,
+ codeLine: 1,
+ stack: [],
+ highlightedNodes: { [curr.value]: 'visiting' }
+ });
+
+ while (curr !== null) {
+ if (curr.left === null) {
+ visited.push(curr.value);
+
+ records.push({
+ currentNode: curr.value,
+ visited: [...visited],
+ threads: [...activeThreads],
+ explanation: `Node ${curr.value} has no left child. Visit node ${curr.value} and move to the right child.`,
+ codeLine: 4,
+ stack: [],
+ highlightedNodes: { [curr.value]: 'visiting' }
+ });
+
+ const nextNode = curr.right;
+ curr = nextNode;
+
+ if (curr) {
+ records.push({
+ currentNode: curr.value,
+ visited: [...visited],
+ threads: [...activeThreads],
+ explanation: `Move curr to its right child pointer -> Node ${curr.value}.`,
+ codeLine: 2,
+ stack: [],
+ highlightedNodes: { [curr.value]: 'visiting' }
+ });
+ }
+ } else {
+ const pred = findPredecessor(curr, curr.left);
+
+ records.push({
+ currentNode: curr.value,
+ predecessor: pred.value,
+ visited: [...visited],
+ threads: [...activeThreads],
+ explanation: `Node ${curr.value} has a left child. Search for its inorder predecessor: rightmost node in left subtree -> Node ${pred.value}.`,
+ codeLine: 6,
+ stack: [],
+ highlightedNodes: { [curr.value]: 'active', [pred.value]: 'predecessor' }
+ });
+
+ const threadIdx = activeThreads.findIndex(t => t.from === pred.value && t.to === curr.value);
+
+ if (threadIdx === -1) {
+ // Predecessor's right is null, establish thread
+ activeThreads.push({ from: pred.value, to: curr.value });
+
+ records.push({
+ currentNode: curr.value,
+ predecessor: pred.value,
+ visited: [...visited],
+ threads: [...activeThreads],
+ explanation: `Predecessor ${pred.value}'s right is null. Establish a temporary Thread (link) from ${pred.value} back to current node ${curr.value} to remember the return path.`,
+ codeLine: 8,
+ stack: [],
+ highlightedNodes: { [curr.value]: 'active', [pred.value]: 'predecessor' }
+ });
+
+ const nextNode = curr.left;
+
+ records.push({
+ currentNode: nextNode.value,
+ predecessor: pred.value,
+ visited: [...visited],
+ threads: [...activeThreads],
+ explanation: `Thread established. Now safe to move current pointer to left child: Node ${nextNode.value}.`,
+ codeLine: 9,
+ stack: [],
+ highlightedNodes: { [nextNode.value]: 'visiting', [pred.value]: 'predecessor' }
+ });
+
+ curr = nextNode;
+ } else {
+ // Thread already exists, clear it and visit current
+ activeThreads = activeThreads.filter(t => !(t.from === pred.value && t.to === curr.value));
+
+ records.push({
+ currentNode: curr.value,
+ predecessor: pred.value,
+ visited: [...visited],
+ threads: [...activeThreads],
+ explanation: `Predecessor ${pred.value}'s right points to ${curr.value}. This indicates the left subtree was already traversed! Remove the temporary Thread to restore the tree.`,
+ codeLine: 11,
+ stack: [],
+ highlightedNodes: { [curr.value]: 'active', [pred.value]: 'predecessor' }
+ });
+
+ visited.push(curr.value);
+
+ records.push({
+ currentNode: curr.value,
+ predecessor: pred.value,
+ visited: [...visited],
+ threads: [...activeThreads],
+ explanation: `Visit node ${curr.value}. Add it to the path: [${visited.join(', ')}].`,
+ codeLine: 12,
+ stack: [],
+ highlightedNodes: { [curr.value]: 'visiting' }
+ });
+
+ const nextNode = curr.right;
+ curr = nextNode;
+
+ if (curr) {
+ records.push({
+ currentNode: curr.value,
+ visited: [...visited],
+ threads: [...activeThreads],
+ explanation: `Now move current pointer to its right child: Node ${curr.value}.`,
+ codeLine: 12,
+ stack: [],
+ highlightedNodes: { [curr.value]: 'visiting' }
+ });
+ }
+ }
+ }
+ }
+
+ records.push({
+ currentNode: null,
+ visited: [...visited],
+ threads: [],
+ explanation: `curr is null! Morris Traversal finished with O(1) extra space!`,
+ codeLine: 15,
+ stack: [],
+ highlightedNodes: {}
+ });
+
+ return records;
+ };
+
+ // Start playback
+ const startVisualizer = () => {
+ if (!root) {
+ setMessage('⚠️ Please insert a node or generate a random tree first!');
+ return;
+ }
+
+ setIsAnimating(true);
+ setQuizSubmitted(false);
+ setSelectedOption(null);
+
+ const preCalculated = preCalculateSteps();
+ setSteps(preCalculated);
+
+ let nextIdx = currentStepIdx === -1 || currentStepIdx >= preCalculated.length - 1 ? 0 : currentStepIdx + 1;
+ setCurrentStepIdx(nextIdx);
+ };
+
+ // Animation effect loop
+ useEffect(() => {
+ if (!isAnimating || steps.length === 0) return;
+
+ if (currentStepIdx >= steps.length) {
+ setIsAnimating(false);
+ return;
+ }
+
+ const currentStep = steps[currentStepIdx];
+ setMessage(currentStep.explanation);
+
+ // Schedule next step
+ timerRef.current = setTimeout(() => {
+ if (currentStepIdx < steps.length - 1) {
+ setCurrentStepIdx(prev => prev + 1);
+ } else {
+ setIsAnimating(false);
+ setMessage(`Traversal Finished! Visited Order: [${currentStep.visited.join(', ')}]`);
+ }
+ }, 1800 / speed);
+
+ return () => {
+ if (timerRef.current) clearTimeout(timerRef.current);
+ };
+ }, [isAnimating, currentStepIdx, steps, speed]);
+
+ // Pause
+ const pauseVisualizer = () => {
+ setIsAnimating(false);
+ if (timerRef.current) clearTimeout(timerRef.current);
+ };
+
+ // Next Step
+ const stepForward = () => {
+ setIsAnimating(false);
+ let preCalculated = steps;
+ if (steps.length === 0) {
+ preCalculated = preCalculateSteps();
+ setSteps(preCalculated);
+ }
+
+ if (currentStepIdx < preCalculated.length - 1) {
+ setCurrentStepIdx(prev => prev + 1);
+ }
+ };
+
+ // Prev Step
+ const stepBackward = () => {
+ setIsAnimating(false);
+ if (currentStepIdx > 0) {
+ setCurrentStepIdx(prev => prev - 1);
+ }
+ };
+
+ // Reset Playback
+ const resetPlayback = () => {
+ setIsAnimating(false);
+ if (timerRef.current) clearTimeout(timerRef.current);
+ setCurrentStepIdx(-1);
+ setSteps([]);
+ setMessage('Playback reset. Click Start Traversal to begin.');
+ };
+
+ const handleResetTree = () => {
+ setRoot(null);
+ resetPlayback();
+ setMessage('Tree has been cleared. Add nodes or click Generate.');
+ };
+
+ // Quiz behaviors
+ const handleQuizAnswer = (idx) => {
+ if (quizSubmitted) return;
+ setSelectedOption(idx);
+ };
+
+ const submitQuiz = () => {
+ if (selectedOption === null) return;
+ setQuizSubmitted(true);
+ };
+
+ const nextQuizQuestion = () => {
+ setSelectedOption(null);
+ setQuizSubmitted(false);
+ const questionsList = quizzes[mode];
+ setQuizIdx(prev => (prev + 1) % questionsList.length);
+ };
+
+ // Compute Tree Layout Coordinates to prevent visual overlaps
+ const calculateCoordinates = (node, x = 400, y = 60, level = 0, nodesList = [], edgesList = []) => {
+ if (!node) return { nodesList, edgesList };
+
+ const nodeRadius = 24;
+ // Exponential horizontal spacing per depth level to guarantee zero child overlaps
+ const xOffset = 260 / Math.pow(2, level);
+ const yOffset = 80;
+
+ const currentStep = steps[currentStepIdx];
+ const highlightedState = currentStep?.highlightedNodes?.[node.value] || null;
+
+ nodesList.push({
+ value: node.value,
+ x,
+ y,
+ state: highlightedState, // 'visiting', 'active', 'predecessor', or null
+ isVisited: currentStep?.visited?.includes(node.value) || false
+ });
+
+ if (node.left) {
+ const leftX = x - xOffset;
+ const leftY = y + yOffset;
+ edgesList.push({
+ x1: x,
+ y1: y + nodeRadius,
+ x2: leftX,
+ y2: leftY - nodeRadius,
+ isMorrisThread: false
+ });
+ calculateCoordinates(node.left, leftX, leftY, level + 1, nodesList, edgesList);
+ }
+
+ if (node.right) {
+ const rightX = x + xOffset;
+ const rightY = y + yOffset;
+ edgesList.push({
+ x1: x,
+ y1: y + nodeRadius,
+ x2: rightX,
+ y2: rightY - nodeRadius,
+ isMorrisThread: false
+ });
+ calculateCoordinates(node.right, rightX, rightY, level + 1, nodesList, edgesList);
+ }
+
+ return { nodesList, edgesList };
+ };
+
+ const buildTreeRenderData = () => {
+ if (!root) return { renderNodes: [], renderEdges: [] };
+ const { nodesList, edgesList } = calculateCoordinates(root);
+
+ // If Morris Traversal is active, render the thread connections dynamically!
+ const currentStep = steps[currentStepIdx];
+ if (currentStep?.threads && currentStep.threads.length > 0) {
+ currentStep.threads.forEach(thread => {
+ const fromNode = nodesList.find(n => n.value === thread.from);
+ const toNode = nodesList.find(n => n.value === thread.to);
+
+ if (fromNode && toNode) {
+ edgesList.push({
+ x1: fromNode.x,
+ y1: fromNode.y,
+ x2: toNode.x,
+ y2: toNode.y,
+ isMorrisThread: true
+ });
+ }
+ });
+ }
+
+ return { renderNodes: nodesList, renderEdges: edgesList };
+ };
+
+ const { renderNodes, renderEdges } = buildTreeRenderData();
+
+ // Find SVG boundary to ensure responsiveness
+ const getSvgDimensions = () => {
+ if (renderNodes.length === 0) return { width: 800, height: 400 };
+ const xCoords = renderNodes.map(n => n.x);
+ const yCoords = renderNodes.map(n => n.y);
+
+ const minX = Math.min(...xCoords);
+ const maxX = Math.max(...xCoords);
+ const maxY = Math.max(...yCoords);
+
+ const padding = 60;
+ const computedWidth = maxX - minX + padding * 2;
+ const computedHeight = maxY + padding * 1.5;
+
+ return {
+ width: Math.max(800, computedWidth),
+ height: Math.max(380, computedHeight),
+ viewBoxOffset: minX - padding
+ };
+ };
+
+ const svgDimensions = getSvgDimensions();
+
+ // Draw custom curves for Morris Traversal threads
+ const drawMorrisCurve = (x1, y1, x2, y2) => {
+ const dx = x2 - x1;
+ const dy = y2 - y1;
+ // Curved line curving to the right / up to differentiate from standard parent-child edges
+ const cx = (x1 + x2) / 2 + 35;
+ const cy = (y1 + y2) / 2 - 30;
+ return `M ${x1} ${y1} Q ${cx} ${cy} ${x2} ${y2}`;
+ };
+
+ // Clean up
+ useEffect(() => {
+ return () => {
+ if (timerRef.current) clearTimeout(timerRef.current);
+ };
+ }, []);
+
+ const currentStep = steps[currentStepIdx] || null;
+ const currentHighlightLine = currentStep ? currentStep.codeLine : -1;
+ const activeComplexity = complexityInfo[mode];
+ const activeQuizList = quizzes[mode];
+ const activeQuestion = activeQuizList[quizIdx];
+
+ return (
+
+
+
+
+
+ {/* Title Block */}
+
+
+
+ Binary Tree Algorithms
+
+
+ {mode.replace('-', ' ')} Traversal
+
+
+ Visualize recursive call stacks, queue progressions, and constant space threaded Morris algorithms.
+
+
+
+ {/* Mode Tabs */}
+
+ {['pre-order', 'in-order', 'post-order', 'level-order', 'morris'].map(tab => (
+ {
+ setMode(tab);
+ resetPlayback();
+ }}
+ className={`px-3 py-1.5 text-xs font-semibold rounded-lg capitalize transition-all ${
+ mode === tab
+ ? 'bg-gradient-to-r from-indigo-600 to-purple-600 text-white shadow-md shadow-indigo-950'
+ : 'text-slate-400 hover:text-slate-200 hover:bg-slate-800/50'
+ }`}
+ >
+ {tab.replace('-', ' ')}
+
+ ))}
+
+
+
+ {/* Dashboard Grid */}
+
+
+ {/* LEFT: Tree Workspace & Controls */}
+
+
+ {/* Control Bar Card */}
+
+ {/* Insert / Generate Controls */}
+
+
+ 🎲 Random Balanced Tree
+
+
+ setInputValue(e.target.value)}
+ placeholder="Value (1-99)"
+ className="w-full sm:w-28 px-3 py-2 text-xs bg-slate-950 border border-slate-800 rounded-xl text-slate-100 placeholder-slate-600 focus:outline-none focus:border-indigo-500 transition-colors"
+ disabled={isAnimating}
+ onKeyDown={(e) => e.key === 'Enter' && handleInsert()}
+ />
+
+ Insert
+
+
+
+
+ {/* Playback Controls */}
+
+
+
+
+
+
+ {isAnimating ? : }
+
+
0 && currentStepIdx >= steps.length - 1}
+ className="p-1.5 text-slate-400 hover:text-slate-200 disabled:opacity-30 rounded-lg"
+ title="Next Step"
+ >
+
+
+
+
+
+
+
+
+ Clear Tree
+
+
+
+ {/* Speed Slider */}
+
+ Speed
+ setSpeed(parseFloat(e.target.value))}
+ className="w-full accent-indigo-500 h-1 bg-slate-800 rounded-lg cursor-pointer"
+ />
+ {speed}x
+
+
+
+ {/* Explanation / Progress Bar */}
+
+
+
+ Current Step Activity
+
+
+ Step {currentStepIdx !== -1 ? currentStepIdx + 1 : 0} / {steps.length || 0}
+
+
+
+ {message}
+
+
+
+ {/* Tree SVG Visualization Area */}
+
+
+ {/* Dynamic Overlay labels */}
+
+
+
+ Current Node (curr)
+
+
+
+ Visited Node
+
+ {mode === 'morris' && (
+ <>
+
+
+ Predecessor (pred)
+
+
+
+ Temporary Thread Link
+
+ >
+ )}
+
+
+ {/* Main SVG Render */}
+ {renderNodes.length > 0 ? (
+
+
+
+
+
+
+
+
+ {/* Render Edges / Thread lines */}
+ {renderEdges.map((edge, idx) => {
+ if (edge.isMorrisThread) {
+ return (
+
+ );
+ }
+ return (
+
+ );
+ })}
+
+ {/* Render Nodes */}
+ {renderNodes.map((node, idx) => {
+ const isCurr = node.state === 'visiting';
+ const isPred = node.state === 'predecessor';
+ const isActive = node.state === 'active';
+ const isVisited = node.isVisited;
+
+ let nodeColor = 'bg-slate-900 border-slate-700 text-slate-300';
+ let fillHex = '#0f172a';
+ let strokeHex = '#334155';
+
+ if (isCurr) {
+ fillHex = '#10b981'; // emerald-500
+ strokeHex = '#34d399';
+ } else if (isPred) {
+ fillHex = '#a855f7'; // purple-500
+ strokeHex = '#c084fc';
+ } else if (isVisited) {
+ fillHex = '#f59e0b'; // amber-500
+ strokeHex = '#fbbf24';
+ } else if (isActive) {
+ fillHex = '#3b82f6'; // blue-500
+ strokeHex = '#60a5fa';
+ }
+
+ return (
+
+ {/* Outer glow ring for curr or predecessor */}
+ {(isCurr || isPred) && (
+
+ )}
+
+ {/* Main node circle */}
+
+
+ {/* Inner text node label */}
+
+ {node.value}
+
+
+ {/* Extra pointers labels inside SVGs */}
+ {isCurr && (
+
+
+ curr
+
+ )}
+
+ );
+ })}
+
+
+ ) : (
+
+
+
Workspace Empty
+
Please generate a random tree or insert custom node elements.
+
+ )}
+
+ {/* BFS Queue or Stack Visualization strip */}
+ {currentStep && (
+
+ {mode === 'level-order' && (
+
+
FIFO Queue:
+
+ {currentStep.queue && currentStep.queue.length > 0 ? (
+ currentStep.queue.map((val, qidx) => (
+
+ {val}
+
+ ))
+ ) : (
+
Queue is empty
+ )}
+
+
+ )}
+
+ {(mode === 'pre-order' || mode === 'in-order' || mode === 'post-order') && (
+
+
Call Stack:
+
+ {currentStep.stack && currentStep.stack.length > 0 ? (
+ currentStep.stack.map((val, sidx) => (
+
+ {val}
+
+ ))
+ ) : (
+
Recursion stack empty (at root level)
+ )}
+
+
+ )}
+
+ {/* Active Traversal Result Path Output */}
+
+
Result Path:
+
+ {currentStep.visited && currentStep.visited.length > 0 ? (
+ currentStep.visited.map((val, pidx) => (
+
+
+ {val}
+
+ {pidx < currentStep.visited.length - 1 && (
+ →
+ )}
+
+ ))
+ ) : (
+
No nodes visited yet. Click Play.
+ )}
+
+
+
+ )}
+
+
+ {/* Complexity Information Panel */}
+
+
+ {/* Time Complexity Card */}
+
+
+
+
+
Time Complexity
+
+
+ {activeComplexity.time}
+
+
+
+ {activeComplexity.timeDesc}
+
+
+
+ {/* Space Complexity Card */}
+
+
+
+
+
Space Complexity
+
+
+ {activeComplexity.space}
+
+
+
+ {activeComplexity.spaceDesc}
+
+
+
+
+
+
+ {/* RIGHT: Code Block & Quiz Modules */}
+
+
+ {/* Interactive Code Highlighter Card */}
+
+
+
+
Pseudocode
+
+
+ {pseudocode[mode].map((line, idx) => {
+ const isHighlighted = idx === currentHighlightLine;
+ return (
+
+ );
+ })}
+
+
+
+ {/* Quiz Challenge Card */}
+
+
+
+
+
+ {activeQuestion.question}
+
+
+ {/* Option list */}
+
+ {activeQuestion.options.map((opt, oIdx) => {
+ const isSelected = selectedOption === oIdx;
+ const isCorrect = oIdx === activeQuestion.answer;
+
+ let btnColor = 'bg-slate-950 hover:bg-slate-800/80 border-slate-800 text-slate-400';
+ if (isSelected) {
+ btnColor = 'bg-indigo-950/40 border-indigo-500 text-indigo-300';
+ }
+ if (quizSubmitted) {
+ if (isCorrect) {
+ btnColor = 'bg-emerald-950/40 border-emerald-500 text-emerald-300';
+ } else if (isSelected) {
+ btnColor = 'bg-rose-950/40 border-rose-500 text-rose-300';
+ }
+ }
+
+ return (
+
handleQuizAnswer(oIdx)}
+ className={`w-full text-left px-4 py-2.5 text-xs font-medium rounded-xl border transition-all flex items-center justify-between gap-2.5 ${btnColor}`}
+ disabled={quizSubmitted}
+ >
+ {opt}
+ {quizSubmitted && isCorrect && }
+ {quizSubmitted && isSelected && !isCorrect && }
+
+ );
+ })}
+
+
+ {/* Submission Controls */}
+
+ {!quizSubmitted ? (
+
+ Submit Answer
+
+ ) : (
+
+ Next Question
+
+ )}
+
+
+ {/* Quiz feedback explanation */}
+ {quizSubmitted && (
+
+
+
+
+ {selectedOption === activeQuestion.answer ? '🎉 Correct Answer!' : '❌ Incorrect Answer'}
+
+ {activeQuestion.explanation}
+
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/app/visualizer/trees/traversing/in-order/animation.jsx b/app/visualizer/trees/traversing/in-order/animation.jsx
index 08c09831f..b2d486fb7 100755
--- a/app/visualizer/trees/traversing/in-order/animation.jsx
+++ b/app/visualizer/trees/traversing/in-order/animation.jsx
@@ -1,7 +1,10 @@
-'use client';
-import React, { useState, useRef, useEffect } from 'react';
-import Navbar from '@/app/components/navbarinner';
-import Footer from '@/app/components/footer';
+"use client";
+
+import React, { useEffect, useRef, useState } from "react";
+import {
+ VisualizerCard,
+ VisualizerInteractiveLayout,
+} from "@/app/visualizer/components/VisualizerInteractiveLayout";
class TreeNode {
constructor(value) {
@@ -13,8 +16,8 @@ class TreeNode {
export default function InOrderVisualizer() {
const [root, setRoot] = useState(null);
- const [inputValue, setInputValue] = useState('');
- const [message, setMessage] = useState('Tree is empty');
+ const [inputValue, setInputValue] = useState("");
+ const [message, setMessage] = useState("Tree is empty");
const [isAnimating, setIsAnimating] = useState(false);
const [highlightedNodes, setHighlightedNodes] = useState([]);
const [traversalResult, setTraversalResult] = useState([]);
@@ -22,45 +25,44 @@ export default function InOrderVisualizer() {
const [steps, setSteps] = useState(0);
const animationRef = useRef(null);
- // Insert node into BST (purely functional, no mutation)
const insertNode = (node, value) => {
if (!node) return new TreeNode(value);
if (value < node.value) {
return { ...node, left: insertNode(node.left, value) };
- } else if (value > node.value) {
+ }
+ if (value > node.value) {
return { ...node, right: insertNode(node.right, value) };
}
return node;
};
const handleInsert = () => {
- const value = parseInt(inputValue);
- if (isNaN(value)) {
- setMessage('Please enter a valid number');
+ const value = parseInt(inputValue, 10);
+ if (Number.isNaN(value)) {
+ setMessage("Please enter a valid number");
return;
}
- setRoot(prev => {
+ setRoot((prev) => {
const newRoot = insertNode(prev, value);
setMessage(`Inserted ${value}`);
return newRoot;
});
- setInputValue('');
+ setInputValue("");
setTraversalResult([]);
setHighlightedNodes([]);
setSteps(0);
};
- // Generate random tree
const generateRandomTree = () => {
- const size = Math.floor(Math.random() * 5) + 5; // 5-9 nodes
- const values = Array.from({length: size}, () => Math.floor(Math.random() * 100) + 1);
-
+ 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(val => {
- newRoot = insertNode(newRoot, val);
+ values.forEach((value) => {
+ newRoot = insertNode(newRoot, value);
});
-
+
setRoot(newRoot);
setMessage(`Generated tree with ${size} nodes`);
setTraversalResult([]);
@@ -68,41 +70,39 @@ export default function InOrderVisualizer() {
setSteps(0);
};
- // Reset everything
const reset = () => {
if (animationRef.current) {
clearTimeout(animationRef.current);
}
setRoot(null);
- setInputValue('');
+ setInputValue("");
setIsAnimating(false);
- setMessage('Tree is empty');
+ setMessage("Tree is empty");
setTraversalResult([]);
setHighlightedNodes([]);
setSteps(0);
};
- // In-order traversal with animation tracking
const inOrderTraversal = (node, path = []) => {
if (!node) return path;
-
+
const leftPath = inOrderTraversal(node.left, path);
- leftPath.push({
- value: node.value,
- action: 'visit',
- highlighted: true
+ leftPath.push({
+ value: node.value,
+ action: "visit",
+ highlighted: true,
});
return inOrderTraversal(node.right, leftPath);
};
const visualizeInOrder = () => {
if (!root) {
- setMessage('Tree is empty!');
+ setMessage("Tree is empty!");
return;
}
setIsAnimating(true);
- setMessage('Performing in-order traversal...');
+ setMessage("Performing in-order traversal...");
setTraversalResult([]);
setHighlightedNodes([]);
setSteps(0);
@@ -114,12 +114,14 @@ export default function InOrderVisualizer() {
if (step < traversalPath.length) {
const current = traversalPath[step];
setHighlightedNodes([current.value]);
- setTraversalResult(prev => [...prev, current.value]);
+ setTraversalResult((prev) => [...prev, current.value]);
setSteps(step + 1);
step++;
animationRef.current = setTimeout(animateStep, 1000 / speed);
} else {
- setMessage(`In-order traversal complete: [${traversalPath.map(n => n.value).join(', ')}]`);
+ setMessage(
+ `In-order traversal complete: [${traversalPath.map((node) => node.value).join(", ")}]`
+ );
setIsAnimating(false);
setHighlightedNodes([]);
}
@@ -128,12 +130,11 @@ export default function InOrderVisualizer() {
animateStep();
};
- // Render tree as SVG with centered layout
const renderTree = (node, x = 400, y = 50, level = 0, nodes = [], edges = []) => {
if (!node) return { nodes, edges };
const nodeRadius = 25;
- const xOffset = Math.max(50, 200 / (level + 1)); // Dynamic spacing
+ const xOffset = Math.max(50, 200 / (level + 1));
const yOffset = 80;
nodes.push({
@@ -172,23 +173,21 @@ export default function InOrderVisualizer() {
const { nodes, edges } = root ? renderTree(root) : { nodes: [], edges: [] };
- // Calculate SVG dimensions based on tree size
const getSvgDimensions = () => {
if (nodes.length === 0) return { width: 800, height: 400 };
-
- const xValues = nodes.map(node => node.x);
- const yValues = nodes.map(node => node.y);
+
+ const xValues = nodes.map((node) => node.x);
+ const yValues = nodes.map((node) => node.y);
const padding = 50;
-
+
return {
width: Math.max(800, Math.max(...xValues) - Math.min(...xValues) + 2 * padding),
- height: Math.max(400, Math.max(...yValues) + 2 * padding)
+ height: Math.max(400, Math.max(...yValues) + 2 * padding),
};
};
const svgDimensions = getSvgDimensions();
- // Clean up on unmount
useEffect(() => {
return () => {
if (animationRef.current) {
@@ -198,183 +197,174 @@ export default function InOrderVisualizer() {
}, []);
return (
-
-
-
-
-
- In-Order Traversal Visualizer
-
-
- Visualize how in-order traversal visits nodes in a binary search tree
-
-
-
- {/* Controls */}
-
-
-
-
+
+
+
+
+ Generate Random Tree
+
+
-
-
- {isAnimating ? "Traversing..." : "Start Traversal"}
-
+ onKeyDown={(e) => e.key === "Enter" && handleInsert()}
+ />
- Reset All
+ Insert
+
+
+ {isAnimating ? "Traversing..." : "Start Traversal"}
+
+
+ Reset All
+
+
+
-
-
-
Speed:
-
setSpeed(parseFloat(e.target.value))}
- className="flex-1"
- disabled={isAnimating}
- />
-
{speed}x
+
+
+ Speed:
+ setSpeed(parseFloat(e.target.value))}
+ className="flex-1"
+ disabled={isAnimating}
+ />
+ {speed}x
+
+
+
-
-
-
Nodes
-
{nodes.length}
-
-
+
-
- {/* Status Message */}
-
- {message}
-
-
- {/* Tree Visualization */}
-
-
Tree Visualization
-
- {nodes.length > 0 ? (
-
-
- {edges.map((edge, i) => (
-
+
+
+ {message}
+
+
+
+ Tree Visualization
+
+ {nodes.length > 0 ? (
+
+
+ {edges.map((edge, i) => (
+
+ ))}
+ {nodes.map((node, i) => (
+
+
- ))}
- {nodes.map((node, i) => (
-
-
-
- {node.value}
-
-
- ))}
-
-
- ) : (
-
- {isAnimating ? "Traversing..." : "No tree generated yet"}
-
- )}
-
-
- {traversalResult.length > 0 && (
-
- Path:
- [{traversalResult.join(', ')}]
+
+ {node.value}
+
+
+ ))}
+
+
+ ) : (
+
+ {isAnimating ? "Traversing..." : "No tree generated yet"}
)}
- {/* Explanation Panel - Now below the tree */}
-
-
About In-Order Traversal
-
-
-
Visits nodes in the order:
-
- Left subtree
- Root node
- Right subtree
-
-
For BSTs, this produces nodes in sorted order.
-
+ {traversalResult.length > 0 && (
+
+ Path:
+
+ [{traversalResult.join(", ")}]
+
+
+ )}
+
+
+
+ About In-Order Traversal
+
+
+
Visits nodes in the order:
+
+ Left subtree
+ Root node
+ Right subtree
+
+
For BSTs, this produces nodes in sorted order.
+
-
-
Algorithm:
-
+
+
Algorithm:
+
{`function inOrder(node) {
if (node !== null) {
inOrder(node.left);
@@ -382,24 +372,21 @@ export default function InOrderVisualizer() {
inOrder(node.right);
}
}`}
-
-
+
+
-
-
-
-
-
+
+
);
-}
\ No newline at end of file
+}
diff --git a/app/visualizer/trees/traversing/in-order/page.jsx b/app/visualizer/trees/traversing/in-order/page.jsx
index 2c87be6e4..4715c83bb 100755
--- a/app/visualizer/trees/traversing/in-order/page.jsx
+++ b/app/visualizer/trees/traversing/in-order/page.jsx
@@ -1,16 +1,14 @@
-import Animation from "@/app/visualizer/trees/traversing/in-order/animation";
+import TreeTraversalVisualizer from "@/app/visualizer/trees/traversing/TreeTraversalVisualizer";
export const metadata = {
- title: 'Tree Visualizer | Learn Tree Data Structures with Animation',
- description: 'Visualize how Tree Data Structures work in DSA with interactive animations. Perfect for beginners and interview prep.',
- keywords: ['Tree DSA', 'Tree Visualizer', 'Learn Tree', 'Binary Tree', 'DSA Animation'],
+ title: 'In-Order Traversal Visualizer | AlgoBuddy',
+ description: 'Visualize In-Order (Left → Root → Right) binary tree traversal step-by-step with interactive animations, pseudocode highlighting, and quizzes.',
+ keywords: ['In-Order Traversal', 'Binary Tree', 'BST', 'DSA Animation', 'Tree Visualizer'],
robots: "index, follow",
};
-const TreeVisualizer = () => {
- return (
-
- );
+const InOrderPage = () => {
+ return
;
};
-export default TreeVisualizer;
\ No newline at end of file
+export default InOrderPage;
diff --git a/app/visualizer/trees/traversing/level-order/page.jsx b/app/visualizer/trees/traversing/level-order/page.jsx
new file mode 100644
index 000000000..d74878458
--- /dev/null
+++ b/app/visualizer/trees/traversing/level-order/page.jsx
@@ -0,0 +1,14 @@
+import TreeTraversalVisualizer from "@/app/visualizer/trees/traversing/TreeTraversalVisualizer";
+
+export const metadata = {
+ title: 'Level-Order (BFS) Traversal Visualizer | AlgoBuddy',
+ description: 'Visualize Level-Order (Breadth-First Search) binary tree traversal step-by-step with live queue animations, pseudocode highlighting, and quizzes.',
+ keywords: ['Level-Order Traversal', 'BFS', 'Binary Tree', 'Breadth First Search', 'DSA Animation', 'Tree Visualizer'],
+ robots: "index, follow",
+};
+
+const LevelOrderPage = () => {
+ return
;
+};
+
+export default LevelOrderPage;
diff --git a/app/visualizer/trees/traversing/morris/page.jsx b/app/visualizer/trees/traversing/morris/page.jsx
new file mode 100644
index 000000000..e9e190236
--- /dev/null
+++ b/app/visualizer/trees/traversing/morris/page.jsx
@@ -0,0 +1,14 @@
+import TreeTraversalVisualizer from "@/app/visualizer/trees/traversing/TreeTraversalVisualizer";
+
+export const metadata = {
+ title: 'Morris Traversal Visualizer | AlgoBuddy',
+ description: 'Visualize Morris Traversal (threaded binary tree, O(1) space) step-by-step with dynamic thread link animations, pseudocode highlighting, and quizzes.',
+ keywords: ['Morris Traversal', 'Threaded Binary Tree', 'O(1) Space', 'Binary Tree', 'DSA Animation', 'Tree Visualizer'],
+ robots: "index, follow",
+};
+
+const MorrisPage = () => {
+ return
;
+};
+
+export default MorrisPage;
diff --git a/app/visualizer/trees/traversing/post-order/page.jsx b/app/visualizer/trees/traversing/post-order/page.jsx
new file mode 100644
index 000000000..3a417ccaa
--- /dev/null
+++ b/app/visualizer/trees/traversing/post-order/page.jsx
@@ -0,0 +1,14 @@
+import TreeTraversalVisualizer from "@/app/visualizer/trees/traversing/TreeTraversalVisualizer";
+
+export const metadata = {
+ title: 'Post-Order Traversal Visualizer | AlgoBuddy',
+ description: 'Visualize Post-Order (Left → Right → Root) binary tree traversal step-by-step with interactive animations, pseudocode highlighting, and quizzes.',
+ keywords: ['Post-Order Traversal', 'Binary Tree', 'BST', 'DSA Animation', 'Tree Visualizer'],
+ robots: "index, follow",
+};
+
+const PostOrderPage = () => {
+ return
;
+};
+
+export default PostOrderPage;
diff --git a/app/visualizer/trees/traversing/pre-order/page.jsx b/app/visualizer/trees/traversing/pre-order/page.jsx
new file mode 100644
index 000000000..12adccfa1
--- /dev/null
+++ b/app/visualizer/trees/traversing/pre-order/page.jsx
@@ -0,0 +1,14 @@
+import TreeTraversalVisualizer from "@/app/visualizer/trees/traversing/TreeTraversalVisualizer";
+
+export const metadata = {
+ title: 'Pre-Order Traversal Visualizer | AlgoBuddy',
+ description: 'Visualize Pre-Order (Root → Left → Right) binary tree traversal step-by-step with interactive animations, pseudocode highlighting, and quizzes.',
+ keywords: ['Pre-Order Traversal', 'Binary Tree', 'BST', 'DSA Animation', 'Tree Visualizer'],
+ robots: "index, follow",
+};
+
+const PreOrderPage = () => {
+ return
;
+};
+
+export default PreOrderPage;
diff --git a/lib/rateLimit.js b/lib/rateLimit.js
new file mode 100644
index 000000000..bf3bb6c04
--- /dev/null
+++ b/lib/rateLimit.js
@@ -0,0 +1,66 @@
+import { Ratelimit } from "@upstash/ratelimit";
+import { Redis } from "@upstash/redis";
+
+const WINDOW_SECONDS = 60;
+const MAX_REQUESTS = 5;
+
+// ---------------------------------------------------------------------------
+// In-memory fallback — used when Upstash env vars are not configured.
+// Safe for local development (single process) but NOT for serverless
+// production where multiple Lambda instances each hold an independent Map.
+// ---------------------------------------------------------------------------
+const memoryBuckets = new Map();
+
+function checkMemory(key) {
+ const now = Date.now();
+ const bucket = memoryBuckets.get(key);
+ if (!bucket || bucket.resetAt <= now) {
+ memoryBuckets.set(key, {
+ count: 1,
+ resetAt: now + WINDOW_SECONDS * 1_000,
+ });
+ return { allowed: true, remaining: MAX_REQUESTS - 1 };
+ }
+ if (bucket.count >= MAX_REQUESTS) {
+ return { allowed: false, remaining: 0 };
+ }
+ bucket.count += 1;
+ return { allowed: true, remaining: MAX_REQUESTS - bucket.count };
+}
+
+// ---------------------------------------------------------------------------
+// Upstash Redis sliding-window rate limiter — enforced globally across all
+// serverless instances. Requires UPSTASH_REDIS_REST_URL and
+// UPSTASH_REDIS_REST_TOKEN environment variables (set in Vercel project
+// settings; see EnvExample.txt for documentation).
+// ---------------------------------------------------------------------------
+const ratelimit =
+ process.env.UPSTASH_REDIS_REST_URL && process.env.UPSTASH_REDIS_REST_TOKEN
+ ? new Ratelimit({
+ redis: Redis.fromEnv(),
+ limiter: Ratelimit.slidingWindow(MAX_REQUESTS, `${WINDOW_SECONDS} s`),
+ })
+ : null;
+
+/**
+ * Checks whether the given identifier (e.g. "contact:1.2.3.4") should be
+ * allowed to proceed.
+ *
+ * In production (Upstash configured): enforced across all Lambda instances via
+ * a shared Redis store — bypassing the rate limit by hitting different
+ * instances is not possible.
+ *
+ * In local development (Upstash not configured): falls back to a process-local
+ * Map. The limit still works correctly in a single-process dev server.
+ *
+ * @param {string} key A namespaced identifier, e.g. "contact:
"
+ * @returns {Promise<{ allowed: boolean, remaining: number }>}
+ */
+export async function checkRateLimit(key) {
+ if (!ratelimit) {
+ return checkMemory(key);
+ }
+
+ const { success, remaining } = await ratelimit.limit(key);
+ return { allowed: success, remaining: remaining ?? 0 };
+}
diff --git a/lib/supabase.js b/lib/supabase.js
index 75d9e53b3..00caa4600 100755
--- a/lib/supabase.js
+++ b/lib/supabase.js
@@ -1,15 +1,103 @@
-import { createClient } from "@supabase/supabase-js";
+import { createBrowserClient } from "@supabase/ssr";
-const supabaseUrl =
- process.env.NEXT_PUBLIC_SUPABASE_URL || "https://placeholder.supabase.co";
-const supabaseAnonKey =
- process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || "placeholder-key";
+const SUPABASE_ENV_ERROR =
+ "Missing NEXT_PUBLIC_SUPABASE_URL and/or NEXT_PUBLIC_SUPABASE_ANON_KEY. Copy EnvExample.txt to .env.local and add your Supabase project URL and anon key.";
+function isValidHttpUrl(value) {
+ try {
+ const parsed = new URL(value);
+ return parsed.protocol === "http:" || parsed.protocol === "https:";
+ } catch {
+ return false;
+ }
+}
+
+function getSupabaseConfig() {
+ const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
+ const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
+
+ if (!supabaseUrl || !supabaseAnonKey || !isValidHttpUrl(supabaseUrl)) {
+ return null;
+ }
+
+ return { supabaseUrl, supabaseAnonKey };
+}
+
+function createMissingQueryBuilder() {
+ const result = {
+ data: null,
+ error: new Error(SUPABASE_ENV_ERROR),
+ };
+
+ const builder = {
+ then(onFulfilled, onRejected) {
+ return Promise.resolve(result).then(onFulfilled, onRejected);
+ },
+ catch(onRejected) {
+ return Promise.resolve(result).catch(onRejected);
+ },
+ finally(onFinally) {
+ return Promise.resolve(result).finally(onFinally);
+ },
+ };
+
+ return new Proxy(builder, {
+ get(target, prop) {
+ if (prop in target) return target[prop];
+ return () => builder;
+ },
+ });
+}
+
+function createMissingAuthClient() {
+ const missing = async () => ({
+ data: null,
+ error: new Error(SUPABASE_ENV_ERROR),
+ });
+
+ return {
+ getUser: async () => ({ data: { user: null }, error: new Error(SUPABASE_ENV_ERROR) }),
+ getSession: async () => ({ data: { session: null }, error: new Error(SUPABASE_ENV_ERROR) }),
+ signOut: missing,
+ signInWithOAuth: missing,
+ signInWithPassword: missing,
+ signUp: missing,
+ onAuthStateChange: () => ({
+ data: {
+ subscription: {
+ unsubscribe() {},
+ },
+ },
+ }),
+ };
+}
+
+function createMissingSupabaseClient() {
+ return {
+ auth: createMissingAuthClient(),
+ from() {
+ return createMissingQueryBuilder();
+ },
+ rpc() {
+ return createMissingQueryBuilder();
+ },
+ };
+}
+
+// Singleton browser client — stores the session in cookies (shared with the
+// server-side createServerClient) instead of localStorage.
const globalForSupabase = globalThis;
+const supabaseConfig = getSupabaseConfig();
+
export const supabase =
globalForSupabase.__algobuddySupabase ||
- createClient(supabaseUrl, supabaseAnonKey);
+ (supabaseConfig
+ ? createBrowserClient(
+ supabaseConfig.supabaseUrl,
+ supabaseConfig.supabaseAnonKey,
+ )
+ : createMissingSupabaseClient());
if (process.env.NODE_ENV !== "production") {
globalForSupabase.__algobuddySupabase = supabase;
diff --git a/middleware.js b/middleware.js
new file mode 100644
index 000000000..13c1e3a5a
--- /dev/null
+++ b/middleware.js
@@ -0,0 +1,75 @@
+import { createServerClient } from "@supabase/ssr";
+import { NextResponse } from "next/server";
+
+const SUPABASE_ENV_ERROR =
+ "Missing NEXT_PUBLIC_SUPABASE_URL and/or NEXT_PUBLIC_SUPABASE_ANON_KEY. Copy EnvExample.txt to .env.local and add your Supabase project URL and anon key.";
+
+function isValidHttpUrl(value) {
+ try {
+ const parsed = new URL(value);
+ return parsed.protocol === "http:" || parsed.protocol === "https:";
+ } catch {
+ return false;
+ }
+}
+
+function getSupabaseConfig() {
+ const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
+ const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
+
+ if (!supabaseUrl || !supabaseAnonKey || !isValidHttpUrl(supabaseUrl)) {
+ return null;
+ }
+
+ return { supabaseUrl, supabaseAnonKey };
+}
+
+export async function middleware(request) {
+ let supabaseResponse = NextResponse.next({ request });
+
+ const supabaseConfig = getSupabaseConfig();
+ if (!supabaseConfig) {
+ if (process.env.NODE_ENV !== "production") {
+ console.warn(SUPABASE_ENV_ERROR);
+ }
+ return supabaseResponse;
+ }
+
+ // A server client is created per-request so that session cookies can be
+ // refreshed when the access token is close to expiry. The setAll callback
+ // propagates the new cookies into both the outgoing request and response so
+ // that server components and client components see consistent session state.
+ const supabase = createServerClient(
+ supabaseConfig.supabaseUrl,
+ supabaseConfig.supabaseAnonKey,
+ {
+ cookies: {
+ getAll() {
+ return request.cookies.getAll();
+ },
+ setAll(cookiesToSet) {
+ cookiesToSet.forEach(({ name, value }) =>
+ request.cookies.set(name, value),
+ );
+ supabaseResponse = NextResponse.next({ request });
+ cookiesToSet.forEach(({ name, value, options }) =>
+ supabaseResponse.cookies.set(name, value, options),
+ );
+ },
+ },
+ },
+ );
+
+ // Calling getUser() triggers a token refresh if the access token is expired.
+ // This must not be removed — without it, sessions silently expire mid-browse.
+ await supabase.auth.getUser();
+
+ return supabaseResponse;
+}
+
+export const config = {
+ matcher: [
+ // Run on all routes except Next.js internals and static file extensions.
+ "/((?!_next/static|_next/image|favicon\\.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)",
+ ],
+};
diff --git a/next.config.mjs b/next.config.mjs
index 4678774e6..4e6158866 100755
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -1,4 +1,64 @@
+
/** @type {import('next').NextConfig} */
-const nextConfig = {};
+
+const securityHeaders = [
+ // Prevent browsers from rendering the page inside a frame or iframe — closes
+ // clickjacking attack surface on the login and dashboard pages.
+ { key: "X-Frame-Options", value: "DENY" },
+
+ // Stop browsers from MIME-sniffing responses away from the declared
+ // Content-Type, blocking content-injection via mistyped uploads.
+ { key: "X-Content-Type-Options", value: "nosniff" },
+
+ // Only send the origin (no path/query) as the Referer on cross-origin
+ // requests; send the full URL within the same origin.
+ { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
+
+ // Enforce HTTPS for two years, covering all sub-domains, and opt in to
+ // browser preload lists so the policy takes effect before the first request.
+ {
+ key: "Strict-Transport-Security",
+ value: "max-age=63072000; includeSubDomains; preload",
+ },
+
+ // Disable access to hardware APIs that this app never needs.
+ {
+ key: "Permissions-Policy",
+ value: "camera=(), microphone=(), geolocation=()",
+ },
+
+ // Content Security Policy — allows exactly the origins this app loads:
+ // scripts : GA tag manager, Cloudflare Turnstile widget, Vercel Speed Insights
+ // styles : Google Fonts CSS (+ inline Tailwind)
+ // fonts : Google Fonts static files
+ // connect : Supabase (auth + DB), Google Analytics, Cloudflare Turnstile verify,
+ // Vercel Speed Insights reporting
+ // frames : Cloudflare Turnstile renders inside an iframe
+ // frame-ancestors 'none' mirrors X-Frame-Options for CSP-aware browsers
+ {
+ key: "Content-Security-Policy",
+ value: [
+ "default-src 'self'",
+ "script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://challenges.cloudflare.com https://va.vercel-scripts.com",
+ "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
+ "font-src 'self' https://fonts.gstatic.com",
+ "img-src 'self' data: https:",
+ "connect-src 'self' https://*.supabase.co https://www.google-analytics.com https://challenges.cloudflare.com https://va.vercel-scripts.com",
+ "frame-src https://challenges.cloudflare.com",
+ "frame-ancestors 'none'",
+ ].join("; "),
+ },
+];
+
+const nextConfig = {
+ async headers() {
+ return [
+ {
+ source: "/(.*)",
+ headers: securityHeaders,
+ },
+ ];
+ },
+};
export default nextConfig;
diff --git a/package-lock.json b/package-lock.json
index 708a524a3..1016670e8 100755
--- a/package-lock.json
+++ b/package-lock.json
@@ -18,6 +18,8 @@
"@tsparticles/engine": "^3.8.1",
"@tsparticles/react": "^3.0.0",
"@tsparticles/slim": "^3.8.1",
+ "@upstash/ratelimit": "^2.0.8",
+ "@upstash/redis": "^1.38.0",
"@vercel/speed-insights": "^1.2.0",
"axios": "^1.9.0",
"dotenv": "^16.5.0",
@@ -2661,6 +2663,39 @@
"win32"
]
},
+ "node_modules/@upstash/core-analytics": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/@upstash/core-analytics/-/core-analytics-0.0.10.tgz",
+ "integrity": "sha512-7qJHGxpQgQr9/vmeS1PktEwvNAF7TI4iJDi8Pu2CFZ9YUGHZH4fOP5TfYlZ4aVxfopnELiE4BS4FBjyK7V1/xQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@upstash/redis": "^1.28.3"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@upstash/ratelimit": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/@upstash/ratelimit/-/ratelimit-2.0.8.tgz",
+ "integrity": "sha512-YSTMBJ1YIxsoPkUMX/P4DDks/xV5YYCswWMamU8ZIfK9ly6ppjRnVOyBhMDXBmzjODm4UQKcxsJPvaeFAijp5w==",
+ "license": "MIT",
+ "dependencies": {
+ "@upstash/core-analytics": "^0.0.10"
+ },
+ "peerDependencies": {
+ "@upstash/redis": "^1.34.3"
+ }
+ },
+ "node_modules/@upstash/redis": {
+ "version": "1.38.0",
+ "resolved": "https://registry.npmjs.org/@upstash/redis/-/redis-1.38.0.tgz",
+ "integrity": "sha512-wu+dZBptlLy0+MCUEoHmzrY/TnmgDey3+c7EbIGwrLqAvkP8yi5MWZHYGIFtAygmL4Bkz2TdFu+eU0vFPncIcg==",
+ "license": "MIT",
+ "dependencies": {
+ "uncrypto": "^0.1.3"
+ }
+ },
"node_modules/@vercel/speed-insights": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@vercel/speed-insights/-/speed-insights-1.2.0.tgz",
@@ -9792,6 +9827,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/uncrypto": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz",
+ "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==",
+ "license": "MIT"
+ },
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
diff --git a/package.json b/package.json
index e7e1121e4..2a9cb065b 100755
--- a/package.json
+++ b/package.json
@@ -21,6 +21,8 @@
"@tsparticles/engine": "^3.8.1",
"@tsparticles/react": "^3.0.0",
"@tsparticles/slim": "^3.8.1",
+ "@upstash/ratelimit": "^2.0.8",
+ "@upstash/redis": "^1.38.0",
"@vercel/speed-insights": "^1.2.0",
"axios": "^1.9.0",
"dotenv": "^16.5.0",
diff --git a/postcss.config.mjs b/postcss.config.mjs
deleted file mode 100755
index c7bcb4b1e..000000000
--- a/postcss.config.mjs
+++ /dev/null
@@ -1,5 +0,0 @@
-const config = {
- plugins: ["@tailwindcss/postcss"],
-};
-
-export default config;
diff --git a/public/sitemap-0.xml b/public/sitemap-0.xml
index ff58dd076..ee1a153b9 100755
--- a/public/sitemap-0.xml
+++ b/public/sitemap-0.xml
@@ -1,59 +1,121 @@
-https://algobuddy.vercel.app 2026-05-18T06:21:08.429Z
-https://algobuddy.vercel.app/blogs 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/blogs/Content/dsaDifferent 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/blogs/Content/dsaWebDev 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/blogs/Content/timeRequired 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/blogs/Content/whatIsDS 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/dashboard 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/icon.svg 2026-05-18T06:21:08.429Z
-https://algobuddy.vercel.app/login 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/privacy 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/terms 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/graph/algorithms/dijkstra 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/graph/algorithms/kruskal 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/graph/algorithms/prim 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/graph/algorithms/topological-sort 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/graph/representation/adjacency-list 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/graph/representation/adjacency-matrix 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/graph/traversal/bfs 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/graph/traversal/dfs 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/linkedList/operations/comparison 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/linkedList/operations/deletion 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/linkedList/operations/insertion 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/linkedList/operations/merge 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/linkedList/operations/reverse 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/linkedList/operations/traversal 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/linkedList/types/circular 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/linkedList/types/doubly 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/linkedList/types/singly 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/queue/implementation/array 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/queue/implementation/linkedList 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/queue/operations/enqueue-dequeue 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/queue/operations/isempty 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/queue/operations/isfull 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/queue/operations/peek-front 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/queue/types/circular 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/queue/types/deque 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/queue/types/priority 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/queue/types/singleEnded 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/searching/binarysearch 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/searching/linearsearch 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/sorting/bubblesort 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/sorting/insertionsort 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/sorting/mergesort 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/sorting/quicksort 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/sorting/selectionsort 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/stack/implementation/usingArray 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/stack/implementation/usingLinkedList 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/stack/isempty 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/stack/isfull 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/stack/peek 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/stack/polish/postfix 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/stack/polish/prefix 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/stack/push-pop 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/trees/binaryTree/types 2026-05-18T06:21:08.437Z
-https://algobuddy.vercel.app/visualizer/trees/traversing/in-order 2026-05-18T06:21:08.437Z
+https://algobuddy.vercel.app 2026-05-22T09:53:01.091Z
+https://algobuddy.vercel.app/blogs 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/blogs/Content/dsaDifferent 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/blogs/Content/dsaWebDev 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/blogs/Content/timeRequired 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/blogs/Content/whatIsDS 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/dashboard 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/icon.svg 2026-05-22T09:53:01.091Z
+https://algobuddy.vercel.app/login 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/privacy 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/terms 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/dry-run 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/graph/algorithms/dijkstra 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/graph/algorithms/kruskal 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/graph/algorithms/prim 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/graph/algorithms/topological-sort 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/graph/representation/adjacency-list 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/graph/representation/adjacency-matrix 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/graph/traversal/bfs 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/graph/traversal/dfs 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/linkedList/operations/comparison 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/linkedList/operations/deletion 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/linkedList/operations/insertion 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/linkedList/operations/merge 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/linkedList/operations/reverse 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/linkedList/operations/traversal 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/linkedList/types/circular 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/linkedList/types/doubly 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/linkedList/types/singly 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/queue/implementation/array 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/queue/implementation/linkedList 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/queue/operations/enqueue-dequeue 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/queue/operations/isempty 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/queue/operations/isfull 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/queue/operations/peek-front 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/queue/types/circular 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/queue/types/deque 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/queue/types/priority 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/queue/types/singleEnded 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/searching/binarysearch 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/searching/linearsearch 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/sorting/bubblesort 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/sorting/insertionsort 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/sorting/mergesort 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/sorting/quicksort 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/sorting/selectionsort 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/stack/implementation/usingArray 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/stack/implementation/usingLinkedList 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/stack/isempty 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/stack/isfull 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/stack/peek 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/stack/polish/postfix 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/stack/polish/prefix 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/stack/push-pop 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/trees/binaryTree/types 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app/visualizer/trees/traversing/in-order 2026-05-22T09:53:01.111Z
+https://algobuddy.vercel.app 2026-05-21T19:51:29.855Z
+https://algobuddy.vercel.app/blogs 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/blogs/Content/dsaDifferent 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/blogs/Content/dsaWebDev 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/blogs/Content/timeRequired 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/blogs/Content/whatIsDS 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/dashboard 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/icon.svg 2026-05-21T19:51:29.856Z
+https://algobuddy.vercel.app/login 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/privacy 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/terms 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/dry-run 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/graph/algorithms/dijkstra 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/graph/algorithms/kruskal 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/graph/algorithms/prim 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/graph/algorithms/topological-sort 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/graph/representation/adjacency-list 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/graph/representation/adjacency-matrix 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/graph/traversal/bfs 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/graph/traversal/dfs 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/linkedList/operations/comparison 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/linkedList/operations/deletion 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/linkedList/operations/insertion 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/linkedList/operations/merge 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/linkedList/operations/reverse 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/linkedList/operations/traversal 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/linkedList/types/circular 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/linkedList/types/doubly 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/linkedList/types/singly 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/queue/implementation/array 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/queue/implementation/linkedList 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/queue/operations/enqueue-dequeue 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/queue/operations/isempty 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/queue/operations/isfull 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/queue/operations/peek-front 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/queue/types/circular 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/queue/types/deque 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/queue/types/priority 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/queue/types/singleEnded 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/searching/binarysearch 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/searching/linearsearch 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/sorting/bubblesort 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/sorting/insertionsort 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/sorting/mergesort 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/sorting/quicksort 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/sorting/selectionsort 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/stack/implementation/usingArray 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/stack/implementation/usingLinkedList 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/stack/isempty 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/stack/isfull 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/stack/peek 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/stack/polish/postfix 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/stack/polish/prefix 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/stack/push-pop 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/trees/binaryTree/types 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/trees/traversing/in-order 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/trees/traversing/level-order 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/trees/traversing/morris 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/trees/traversing/post-order 2026-05-21T19:51:29.895Z
+https://algobuddy.vercel.app/visualizer/trees/traversing/pre-order 2026-05-21T19:51:29.895Z
\ No newline at end of file
diff --git a/utils/storage.js b/utils/storage.js
new file mode 100644
index 000000000..c256ffdf8
--- /dev/null
+++ b/utils/storage.js
@@ -0,0 +1,27 @@
+export const saveToStorage = (key, value) => {
+ if (typeof window !== "undefined") {
+ localStorage.setItem(key, JSON.stringify(value));
+ }
+};
+
+export const loadFromStorage = (key, fallback) => {
+ if (typeof window !== "undefined") {
+ const item = localStorage.getItem(key);
+
+ if (item) {
+ try {
+ return JSON.parse(item);
+ } catch {
+ return fallback;
+ }
+ }
+ }
+
+ return fallback;
+};
+
+export const removeFromStorage = (key) => {
+ if (typeof window !== "undefined") {
+ localStorage.removeItem(key);
+ }
+};
\ No newline at end of file