From b300ab915f3acd0595098f3b2ae9a298a21525dd Mon Sep 17 00:00:00 2001 From: indar suthar Date: Thu, 13 Nov 2025 00:14:09 +0530 Subject: [PATCH] feat: added tree traversal visualization --- public/Tree-Traversal.png | Bin 0 -> 30745 bytes src/App.jsx | 2 + src/algorithms/Tree/treeTraversal.js | 278 ++++++++++++++++ .../Tree/TreeTraversalVisualizer.jsx | 296 +++++++++++++++++ src/pages/Homepage.jsx | 9 + src/pages/Tree/TreeTraversal.jsx | 303 ++++++++++++++++++ src/pages/Tree/Treepage.jsx | 67 ++++ 7 files changed, 955 insertions(+) create mode 100644 public/Tree-Traversal.png create mode 100644 src/algorithms/Tree/treeTraversal.js create mode 100644 src/components/Tree/TreeTraversalVisualizer.jsx create mode 100644 src/pages/Tree/TreeTraversal.jsx create mode 100644 src/pages/Tree/Treepage.jsx diff --git a/public/Tree-Traversal.png b/public/Tree-Traversal.png new file mode 100644 index 0000000000000000000000000000000000000000..73e299323d534da5f9f7072629f0eef91b16bee9 GIT binary patch literal 30745 zcmbrl1yo$YvM4%)AW3k7y9al7cXu0t2KT`=xVu|`;O;OC?ry;?xI=J%eJs_w4p>fO~H`bRaYoDc~I{6f`Q-YcGKPjlp-&P=9&--vtKt9rSxRDEPO!zYYJ=0tNm4 z9Skho>kQ8s!bA0eQ;zyj%;u0*%E0(I z9(GdCt^UlA7{oayY<4mSKTs?F!;V-<+piI$#NGRGUW1}OoH+y<+Cbz`2ilK>oN$Cg9&&znmN zi#w;wLv_)&(bfRia))eYOpX6UAMlajX#8N^XmC+mV{5GOkZrT_fdY3VdQsRl8@3

Pux)C&8LQ$X@y%yl30 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�@gX?sX=;uv<6pS|K=rIdmtCwX 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ℑ#4baC_gikg(s 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 literal 0 HcmV?d00001 diff --git a/src/App.jsx b/src/App.jsx index 71a3bf5..8519929 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -8,6 +8,7 @@ import DSPage from "./pages/dataStructure/datastructurePage.jsx" import DynamicProgrammingPage from "./pages/dynamic-programming/DyanmicProgrammingPage.jsx"; import Searchingpage from "./pages/searching/searchingPage"; import RecursionPage from "./pages/Recursion/RecursionPage"; +import Treepage from "./pages/Tree/Treepage"; function App() { return ( @@ -20,6 +21,7 @@ function App() { } /> } /> }/> + } /> ); diff --git a/src/algorithms/Tree/treeTraversal.js b/src/algorithms/Tree/treeTraversal.js new file mode 100644 index 0000000..8a2ebfb --- /dev/null +++ b/src/algorithms/Tree/treeTraversal.js @@ -0,0 +1,278 @@ +function buildTreeFromLevelOrder(arr) { + if (!arr || arr.length === 0 || arr[0] === null) return null; + + const nodes = arr.map((val, idx) => + val === null || val === -1 + ? null + : { id: `n${idx}`, value: val, left: null, right: null } + ); + const root = nodes[0]; + if (!root) return null; + + const queue = [root]; + let i = 1; + + while (queue.length > 0 && i < nodes.length) { + const current = queue.shift(); + if (i < nodes.length && nodes[i] !== null) { + current.left = nodes[i]; + queue.push(nodes[i]); + } + i++; + + if (i < nodes.length && nodes[i] !== null) { + current.right = nodes[i]; + queue.push(nodes[i]); + } + i++; + } + return root; +} +function cloneTree(node) { + if (!node) return null; + return { + id: node.id, + value: node.value, + left: cloneTree(node.left), + right: cloneTree(node.right) + }; +} +function getAllNodeIds(node) { + if (!node) return []; + return [node.id, ...getAllNodeIds(node.left), ...getAllNodeIds(node.right)]; +} + +//inorder +export function* inorderTraversal(root) { + if (!root) { + yield {type: "error", message: "Tree is empty", current: null, visited: [], path: [], subtree: "root" }; + return; + } + + const visited = []; + const path = []; + function* traverse(node) { + if (!node) return; + if (node.left) { + path.push(node.value); + yield { + type: "traverse_left", + message: `Moving to left subtree of ${node.value}`, + current: node.value, visited: [...visited], path: [...path], subtree: "left" + }; + yield* traverse(node.left); + path.pop(); + } + path.push(node.value); + visited.push(node.value); + yield { + type: "visit", + message: `Visiting ${node.value} (Inorder: Left → Root → Right)`, + current: node.value, visited: [...visited], path: [...path], subtree: "root" + }; + if (node.right) { + yield { + type: "traverse_right", + message: `Moving to right subtree of ${node.value}`, + current: node.value, visited: [...visited], path: [...path], subtree: "root" + }; + yield* traverse(node.right); + } + + path.pop(); + } + yield* traverse(root); + + yield { + type: "complete", + message: `Inorder traversal complete: ${visited.join(" → ")}`, + current: null, visited: visited, path: [] + }; +} + +//preorder +export function* preorderTraversal(root) { + if (!root) { + yield { type: "error", message: "Tree is empty", current: null, visited: [], path: [] }; + return; + } + const visited = []; + const path = []; + + function* traverse(node) { + if (!node) return; + path.push(node.value); + visited.push(node.value); + yield { + type: "visit", + message: `Visiting ${node.value} (Preorder: Root → Left → Right)`, + current: node.value, visited: [...visited], path: [...path], subtree: "root" + }; + if (node.left) { + yield { + type: "traverse_left", + message: `Moving to left subtree of ${node.value}`, + current: node.value, visited: [...visited],path: [...path] + }; + yield* traverse(node.left); + } + + if (node.right) { + yield { + type: "traverse_right", + message: `Moving to right subtree of ${node.value}`, + current: node.value, visited: [...visited], path: [...path] }; + yield* traverse(node.right); + } + path.pop(); + } + + yield* traverse(root); + + yield { type: "complete", message: `Preorder traversal complete: ${visited.join(" → ")}`, current: null, visited: visited, path: []}; +} + +//postorder +export function* postorderTraversal(root) { + if (!root) { + yield { type: "error", message: "Tree is empty", current: null, visited: [], path: [] }; + return; + } + const visited = []; + const path = []; + + function* traverse(node) { + if (!node) return; + path.push(node.value); + if (node.left) { + yield { + type: "traverse_left", + message: `Moving to left subtree of ${node.value}`, + current: node.value, + visited: [...visited], + path: [...path] + }; + yield* traverse(node.left); + } + + if (node.right) { + yield { + type: "traverse_right", + message: `Moving to right subtree of ${node.value}`, + current: node.value, + visited: [...visited], + path: [...path] + }; + yield* traverse(node.right); + } + visited.push(node.value); + yield { + type: "visit", + message: `Visiting ${node.value} (Postorder: Left → Right → Root)`, + current: node.value, + visited: [...visited], + path: [...path], + subtree: "root" + }; + path.pop(); + } + yield* traverse(root); + yield { + type: "complete", + message: `Postorder traversal complete: ${visited.join(" → ")}`, + current: null, + visited: visited, + path: [] + }; +} +export function* treeTraversalGenerator(treeData, traversalType = "all") { + const root = buildTreeFromLevelOrder(treeData); + if (!root) { + yield { + type: "error", + message: "Invalid tree data", + tree: null, + step: null + }; + return; + } + if (traversalType === "all") { + const traversals = [ + { name: "inorder", generator: inorderTraversal }, + { name: "preorder", generator: preorderTraversal }, + { name: "postorder", generator: postorderTraversal } + ]; + const results = { + inorder: { visited: [], steps: [] }, + preorder: { visited: [], steps: [] }, + postorder: { visited: [], steps: [] } + }; + for (const { name, generator } of traversals) { + for (const step of generator(cloneTree(root))) { + results[name].steps.push(step); + if (step.type === "complete") { + results[name].visited = step.visited; + } + } + } + yield { + type: "sync_step", + tree: cloneTree(root), + stepIndex: -1, + inorder: { type: "start", message: "Starting Inorder traversal", visited: [], path: [] }, + preorder: { type: "start", message: "Starting Preorder traversal", visited: [], path: [] }, + postorder: { type: "start", message: "Starting Postorder traversal", visited: [], path: [] } + }; + const maxSteps = Math.max( + results.inorder.steps.length, + results.preorder.steps.length, + results.postorder.steps.length + ); + for (let i = 0; i < maxSteps; i++) { + const step = { + type: "sync_step", + tree: cloneTree(root), + stepIndex: i, + inorder: results.inorder.steps[i] || results.inorder.steps[results.inorder.steps.length - 1] || null, + preorder: results.preorder.steps[i] || results.preorder.steps[results.preorder.steps.length - 1] || null, + postorder: results.postorder.steps[i] || results.postorder.steps[results.postorder.steps.length - 1] || null + }; + yield step; + } + yield { + type: "complete", + tree: cloneTree(root), + inorder: results.inorder.visited, + preorder: results.preorder.visited, + postorder: results.postorder.visited, + message: `All traversals complete. Inorder: [${results.inorder.visited.join(", ")}], Preorder: [${results.preorder.visited.join(", ")}], Postorder: [${results.postorder.visited.join(", ")}]` + }; + } else { + let generator; + if (traversalType === "inorder") { + generator = inorderTraversal; + } else if (traversalType === "preorder") { + generator = preorderTraversal; + } else if (traversalType === "postorder") { + generator = postorderTraversal; + } else { + yield { + type: "error", + message: "Invalid traversal type", + tree: null, + step: null + }; + return; + } + for (const step of generator(cloneTree(root))) { + yield { + type: "single_step", + tree: cloneTree(root), + traversal: traversalType, + step: step + }; + } + } +} +export { buildTreeFromLevelOrder, cloneTree, getAllNodeIds }; + diff --git a/src/components/Tree/TreeTraversalVisualizer.jsx b/src/components/Tree/TreeTraversalVisualizer.jsx new file mode 100644 index 0000000..6440e78 --- /dev/null +++ b/src/components/Tree/TreeTraversalVisualizer.jsx @@ -0,0 +1,296 @@ +import React, { useEffect, useRef, useState, useMemo } from "react"; + +export default function TreeTraversalVisualizer({ + tree = null, + currentStep = null, + traversalType = "all", + nodeSize = 60, + gapY = 100, + gapX = 80 +}) { + const containerRef = useRef(null); + const [layoutNodes, setLayoutNodes] = useState([]); + const [containerWidth, setContainerWidth] = useState(1000); + const [containerHeight, setContainerHeight] = useState(600); + const nodeIdMapRef = useRef(new WeakMap()); + const nextIdRef = useRef(1); + const getNodeVizId = (node) => { + if (!node) return null; + if (node.id !== undefined && node.id !== null) return String(node.id); + const map = nodeIdMapRef.current; + if (map.has(node)) return map.get(node); + const id = `viz_${nextIdRef.current++}`; + map.set(node, id); + return id; + }; + useEffect(() => { + if (!tree) { + setLayoutNodes([]); + return; + } + + const rect = containerRef.current?.getBoundingClientRect(); + if (rect) { + setContainerWidth(rect.width || 1000); + setContainerHeight(rect.height || 600); + } + + //tree height + function getTreeHeight(node) { + if (!node) return 0; + return 1 + Math.max( + getTreeHeight(node.left), + getTreeHeight(node.right) + ); + } + + const treeHeight = getTreeHeight(tree); + setContainerHeight(Math.max(treeHeight * gapY + 200, 400)); + const layoutMap = new Map(); + const usableWidth = Math.max(containerWidth - 100, 800); + const maxDepth = treeHeight - 1; + const baseSpacing = Math.max(80, gapX); + const inorderPositions = new Map(); + let inorderIndex = 0; + + function assignInorderPositions(node) { + if (!node) return; + assignInorderPositions(node.left); + inorderPositions.set(node, inorderIndex++); + assignInorderPositions(node.right); + } + assignInorderPositions(tree); + + function assignPositions(node, depth) { + if (!node) return; + + const id = getNodeVizId(node); + const inorderPos = inorderPositions.get(node); + const totalNodes = inorderPositions.size; + + const spacing = usableWidth / (totalNodes + 1); + const x = 50 + spacing * (inorderPos + 1); + const y = depth * gapY + 80; + + layoutMap.set(id, { id, value: node.value, rawNode: node, x, y, depth }); + assignPositions(node.left, depth + 1); + assignPositions(node.right, depth + 1); + } + + assignPositions(tree, 0); + + const layoutArray = Array.from(layoutMap.values()); + setLayoutNodes(layoutArray); + }, [tree, containerWidth, gapY, gapX, nodeSize]); + + useEffect(() => { + const onResize = () => { + const rect = containerRef.current?.getBoundingClientRect(); + if (rect) { + setContainerWidth(rect.width || 1000); + } + }; + window.addEventListener("resize", onResize); + onResize(); + return () => window.removeEventListener("resize", onResize); + }, []); + + // Build edges + const edges = useMemo(() => { + if (!tree || !layoutNodes.length) return []; + const edgesArray = []; + const nodeMap = new Map(layoutNodes.map(n => [n.id, n])); + + function buildEdges(node) { + if (!node) return; + const fromId = getNodeVizId(node); + const fromNode = nodeMap.get(fromId); + + if (!fromNode) return; + if (node.left) { + const toId = getNodeVizId(node.left); + const toNode = nodeMap.get(toId); + if (toNode) { + edgesArray.push({ from: fromNode, to: toNode, side: "left" }); + } + buildEdges(node.left); + } + if (node.right) { + const toId = getNodeVizId(node.right); + const toNode = nodeMap.get(toId); + if (toNode) { + edgesArray.push({ from: fromNode, to: toNode, side: "right" }); + } + buildEdges(node.right); + } + } + buildEdges(tree); + return edgesArray; + }, [tree, layoutNodes]); + + const getNodeClass = (nodeLayout) => { + if (!currentStep) { + return "bg-gray-700 text-white border-2 border-gray-600"; + } + let step = null; + if (currentStep.type === "single_step" && currentStep.step) { + step = currentStep.step; + } else if (currentStep.step) { + step = currentStep.step; + } else { + step = currentStep; + } + if (!step) { + return "bg-gray-700 text-white border-2 border-gray-600"; + } + + const stepType = step.type; + const currentId = step.current; + const path = step.path || []; + const visited = step.visited || []; + const nodeValue = nodeLayout.rawNode?.value ?? nodeLayout.value; + const nodeValueStr = String(nodeValue); + const currentIdStr = currentId ? String(currentId) : null; + + if (currentIdStr && currentIdStr === nodeValueStr) { + if (stepType === "visit") { + return "bg-emerald-500 text-white border-4 border-emerald-300 ring-4 ring-emerald-400 ring-opacity-70 shadow-xl transform scale-110 z-20"; + } else if (stepType === "traverse_left" || stepType === "traverse_right") { + return "bg-blue-500 text-white border-4 border-blue-300 ring-2 ring-blue-400 ring-opacity-60 z-10"; + } + } + const pathMatches = path.some(p => String(p) === nodeValueStr); + if (pathMatches && stepType !== "visit" && (!currentIdStr || currentIdStr !== nodeValueStr)) { + return "bg-indigo-600 text-white border-2 border-indigo-400"; + } + if (stepType === "visit" && visited && visited.length > 0 && visited.includes(nodeValue)) { + if (!currentIdStr || currentIdStr !== nodeValueStr) { + return "bg-violet-600 text-white border-2 border-violet-400 opacity-90"; + } + } + return "bg-gray-700 text-white border-2 border-gray-600"; + }; + + // Render node + const Node = ({ n }) => { + const cls = getNodeClass(n); + const radius = nodeSize / 2; + + return ( +

+ {String(n.value)} +
+ ); + }; + const SvgEdges = () => { + if (!layoutNodes.length) return null; + return ( + + + + + + + + + + + + {edges.map((e, i) => { + const x1 = e.from.x; + const y1 = e.from.y + nodeSize * 0.45; + const x2 = e.to.x; + const y2 = e.to.y - nodeSize * 0.45; + + let step = null; + if (currentStep?.type === "single_step" && currentStep.step) step = currentStep.step; + else if (currentStep?.step) step = currentStep.step; + else step = currentStep; + + const path = step?.path || []; + const fromValue = e.from.rawNode?.value ?? e.from.value; + const toValue = e.to.rawNode?.value ?? e.to.value; + const isInPath = path.some(p => String(p) === String(fromValue)) && + path.some(p => String(p) === String(toValue)); + const midX = (x1 + x2) / 2; + const controlY = Math.min(y1, y2) - 30; + const d = `M ${x1} ${y1} Q ${midX} ${controlY} ${x2} ${y2}`; + + return ( + + ); + })} + + ); + }; + + if (!tree) { + return ( +
+
+
🌳
+
No tree data to visualize
+
+
+ ); + } + const getVisitedArray = () => { + if (!currentStep) return []; + let step = null; + if (currentStep.type === "single_step" && currentStep.step) step = currentStep.step; + else if (currentStep.step) step = currentStep.step; + else step = currentStep; + return step?.visited || []; + }; + const visitedArray = getVisitedArray(); + return ( +
+
+ + {layoutNodes.map((n) => ( + + ))} + {visitedArray.length > 0 && ( +
+
Traversal Sequence:
+
+ {visitedArray.map((val, idx) => { + const isLast = idx === visitedArray.length - 1; + return ( + + + {val} + {idx < visitedArray.length - 1 && ( + )} + + ); + })} +
+
+ )} +
+
+ ); +} + diff --git a/src/pages/Homepage.jsx b/src/pages/Homepage.jsx index c9df667..971c971 100644 --- a/src/pages/Homepage.jsx +++ b/src/pages/Homepage.jsx @@ -71,6 +71,15 @@ const sections = [ link: "/dynamic-programming", flag: false, }, + { + title: "Tree Traversal", + description: + "Visualize inorder, preorder, and postorder tree traversals step by step.", + phase: "Phase 2", + img: "/Tree-Traversal.png", + link: "/tree", + flag: false, + }, ]; const Homepage = () => { diff --git a/src/pages/Tree/TreeTraversal.jsx b/src/pages/Tree/TreeTraversal.jsx new file mode 100644 index 0000000..e3b2052 --- /dev/null +++ b/src/pages/Tree/TreeTraversal.jsx @@ -0,0 +1,303 @@ +import React, { useState, useEffect, useRef } from "react"; +import { Toaster, toast } from "react-hot-toast"; +import { ArrowLeft, Play, Pause, StepForward, RotateCcw, TreePine } from "lucide-react"; +import TreeTraversalVisualizer from "../../components/Tree/TreeTraversalVisualizer"; +import { treeTraversalGenerator, buildTreeFromLevelOrder } from "../../algorithms/Tree/treeTraversal"; + +export default function TreeTraversal() { + const [selectedTraversal, setSelectedTraversal] = useState(""); + const [treeData, setTreeData] = useState([1, 2, 3, 4, 5, null, 6, 7]); + const [inputString, setInputString] = useState("1,2,3,4,5,-1,6,7"); + const [speed, setSpeed] = useState(800); + const [isPlaying, setIsPlaying] = useState(false); + const [currentStep, setCurrentStep] = useState(null); + const [steps, setSteps] = useState([]); + const [stepIndex, setStepIndex] = useState(-1); + const [tree, setTree] = useState(null); + const [finalResult, setFinalResult] = useState([]); + const generatorRef = useRef(null); + const timerRef = useRef(null); + const stepsRef = useRef([]); + + useEffect(() => { + const root = buildTreeFromLevelOrder(treeData); + setTree(root); + if (root && selectedTraversal) { + setSteps([]); + setStepIndex(-1); + setCurrentStep(null); + setIsPlaying(false); + } + }, [treeData, selectedTraversal]); + + const handleInputChange = (e) => { + const value = e.target.value; + setInputString(value); + try { + const parsed = value + .split(",") + .map((p) => + ["-1", "", "null"].includes(p.trim().toLowerCase()) + ? null + : Number(p.trim()) + ); + setTreeData(parsed); + setSteps([]); + setStepIndex(-1); + setCurrentStep(null); + setIsPlaying(false); + } catch (err) { + console.error("Error parsing input:", err); + } + }; + + const loadDemo = () => { + const demo = [1, 2, 3, 4, 5, null, 6, 7, null, null, null, null, null, 8, 9]; + setTreeData(demo); + setInputString("1,2,3,4,5,-1,6,7,-1,-1,-1,-1,-1,8,9"); + setSteps([]); + setStepIndex(-1); + setCurrentStep(null); + setIsPlaying(false); + }; + + useEffect(() => { + if (!selectedTraversal || !tree) return; + setIsPlaying(false); + clearTimeout(timerRef.current); + + const newSteps = []; + generatorRef.current = treeTraversalGenerator(treeData, selectedTraversal); + try { + for (const step of generatorRef.current) newSteps.push(step); + stepsRef.current = newSteps; + setSteps(newSteps); + setStepIndex(0); + setCurrentStep(newSteps[0] || null); + + const lastStep = newSteps.at(-1); + const visited = lastStep?.step?.visited ?? []; + setFinalResult(visited); + } catch (error) { + console.error("Error generating steps:", error); + toast.error("Error generating traversal steps"); + } + }, [selectedTraversal, treeData, tree]); + + useEffect(() => { + if (isPlaying && stepIndex < steps.length - 1) { + timerRef.current = setTimeout(() => { + setStepIndex((prev) => + prev + 1 < steps.length ? prev + 1 : steps.length - 1 + ); + }, speed); + } else if (stepIndex >= steps.length - 1) { + setIsPlaying(false); + } + return () => clearTimeout(timerRef.current); + }, [isPlaying, stepIndex, steps.length, speed]); + + useEffect(() => { + if (stepIndex >= 0 && stepIndex < steps.length) + setCurrentStep(steps[stepIndex]); + }, [stepIndex, steps]); + + const togglePlay = () => { + if (steps.length === 0) return; + if (stepIndex >= steps.length - 1) { + setStepIndex(0); + setIsPlaying(true); + } else setIsPlaying(!isPlaying); + }; + const stepForward = () => { + if (stepIndex < steps.length - 1) { + setStepIndex((p) => p + 1); + setIsPlaying(false); + } + }; + const stepBackward = () => { + if (stepIndex > 0) { + setStepIndex((p) => p - 1); + setIsPlaying(false); + } + }; + const reset = () => { + setIsPlaying(false); + clearTimeout(timerRef.current); + setSteps([]); + setStepIndex(-1); + setCurrentStep(null); + setFinalResult([]); + setSelectedTraversal(""); + }; + + const getTraversalDescription = (type) => + type === "inorder" + ? "Left → Root → Right" + : type === "preorder" + ? "Root → Left → Right" + : "Left → Right → Root"; + + if (!selectedTraversal) { + return ( +
+ +

+ Tree Traversal Algorithms +

+

+ Select a traversal method to visualize how it explores tree nodes — + simple, elegant, and interactive. +

+ +
+ {[ + { + id: "inorder", + title: "Inorder Traversal", + desc: "Left → Root → Right", + }, + { + id: "preorder", + title: "Preorder Traversal", + desc: "Root → Left → Right", + }, + { + id: "postorder", + title: "Postorder Traversal", + desc: "Left → Right → Root", + }, + ].map((t) => ( +
setSelectedTraversal(t.id)} + className={`relative bg-gray-700/80 backdrop-blur-md rounded-2xl p-6 text-center border border-gray-800 cursor-pointer transition-all duration-300 hover:scale-105`} + > +
🌳
+

{t.title}

+

{t.desc}

+
+ ))} +
+
+ ); + } + return ( +
+ + {/* Control Bar */} +
+
+ + + +
+ +
+ {["inorder", "preorder", "postorder"].map((t) => ( + + ))} +
+
+ + setSpeed(Number(e.target.value))} className="accent-blue-500 w-24" /> + {speed}ms +
+
+ + + + +
+
+ +
+
+

+ {selectedTraversal.charAt(0).toUpperCase() + + selectedTraversal.slice(1)}{" "} + Traversal +

+

+ {getTraversalDescription(selectedTraversal)} +

+
+ {finalResult.length > 0 && ( +
+

+ Final Sequence +

+
+ {finalResult.map((v, i) => ( + + + {v} + + {i < finalResult.length - 1 && ( + + )} + + ))} +
+
+ )} +
+
+ {tree ? ( + + ) : ( +
Loading Tree...
+ )} +
+
+ ); +} diff --git a/src/pages/Tree/Treepage.jsx b/src/pages/Tree/Treepage.jsx new file mode 100644 index 0000000..9df62e8 --- /dev/null +++ b/src/pages/Tree/Treepage.jsx @@ -0,0 +1,67 @@ +import React, { useState } from "react"; +import { TreePine } from "lucide-react"; +import TreeTraversal from "./TreeTraversal"; + +export default function Treepage() { + const [selectedAlgo, setSelectedAlgo] = useState(""); + + const renderAlgorithm = () => { + switch (selectedAlgo) { + case "tree-traversal": + return ; + default: + return ( +
+
+
+
+ +
+

+ Tree Algorithm Visualizer +

+

+ Select a tree algorithm from the sidebar to begin + visualization. Watch how nodes are traversed step by step! +

+
+
+
+ ); + } + }; + + return ( +
+ {/* Sidebar */} +
+

+ Tree Panel +

+ + + + + ← Back to Home + +
+
+
+ {renderAlgorithm()} +
+
+
+ ); +}