Skip to content

Commit b300ab9

Browse files
committed
feat: added tree traversal visualization
1 parent deb2d1f commit b300ab9

File tree

7 files changed

+955
-0
lines changed

7 files changed

+955
-0
lines changed

public/Tree-Traversal.png

30 KB
Loading

src/App.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import DSPage from "./pages/dataStructure/datastructurePage.jsx"
88
import DynamicProgrammingPage from "./pages/dynamic-programming/DyanmicProgrammingPage.jsx";
99
import Searchingpage from "./pages/searching/searchingPage";
1010
import RecursionPage from "./pages/Recursion/RecursionPage";
11+
import Treepage from "./pages/Tree/Treepage";
1112
function App() {
1213
return (
1314
<Router>
@@ -20,6 +21,7 @@ function App() {
2021
<Route path="/graph" element={<GraphPage />} />
2122
<Route path="/dynamic-programming" element={<DynamicProgrammingPage />} />
2223
<Route path="/recursion" element={<RecursionPage/>}/>
24+
<Route path="/tree" element={<Treepage />} />
2325
</Routes>
2426
</Router>
2527
);
Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
function buildTreeFromLevelOrder(arr) {
2+
if (!arr || arr.length === 0 || arr[0] === null) return null;
3+
4+
const nodes = arr.map((val, idx) =>
5+
val === null || val === -1
6+
? null
7+
: { id: `n${idx}`, value: val, left: null, right: null }
8+
);
9+
const root = nodes[0];
10+
if (!root) return null;
11+
12+
const queue = [root];
13+
let i = 1;
14+
15+
while (queue.length > 0 && i < nodes.length) {
16+
const current = queue.shift();
17+
if (i < nodes.length && nodes[i] !== null) {
18+
current.left = nodes[i];
19+
queue.push(nodes[i]);
20+
}
21+
i++;
22+
23+
if (i < nodes.length && nodes[i] !== null) {
24+
current.right = nodes[i];
25+
queue.push(nodes[i]);
26+
}
27+
i++;
28+
}
29+
return root;
30+
}
31+
function cloneTree(node) {
32+
if (!node) return null;
33+
return {
34+
id: node.id,
35+
value: node.value,
36+
left: cloneTree(node.left),
37+
right: cloneTree(node.right)
38+
};
39+
}
40+
function getAllNodeIds(node) {
41+
if (!node) return [];
42+
return [node.id, ...getAllNodeIds(node.left), ...getAllNodeIds(node.right)];
43+
}
44+
45+
//inorder
46+
export function* inorderTraversal(root) {
47+
if (!root) {
48+
yield {type: "error", message: "Tree is empty", current: null, visited: [], path: [], subtree: "root" };
49+
return;
50+
}
51+
52+
const visited = [];
53+
const path = [];
54+
function* traverse(node) {
55+
if (!node) return;
56+
if (node.left) {
57+
path.push(node.value);
58+
yield {
59+
type: "traverse_left",
60+
message: `Moving to left subtree of ${node.value}`,
61+
current: node.value, visited: [...visited], path: [...path], subtree: "left"
62+
};
63+
yield* traverse(node.left);
64+
path.pop();
65+
}
66+
path.push(node.value);
67+
visited.push(node.value);
68+
yield {
69+
type: "visit",
70+
message: `Visiting ${node.value} (Inorder: Left → Root → Right)`,
71+
current: node.value, visited: [...visited], path: [...path], subtree: "root"
72+
};
73+
if (node.right) {
74+
yield {
75+
type: "traverse_right",
76+
message: `Moving to right subtree of ${node.value}`,
77+
current: node.value, visited: [...visited], path: [...path], subtree: "root"
78+
};
79+
yield* traverse(node.right);
80+
}
81+
82+
path.pop();
83+
}
84+
yield* traverse(root);
85+
86+
yield {
87+
type: "complete",
88+
message: `Inorder traversal complete: ${visited.join(" → ")}`,
89+
current: null, visited: visited, path: []
90+
};
91+
}
92+
93+
//preorder
94+
export function* preorderTraversal(root) {
95+
if (!root) {
96+
yield { type: "error", message: "Tree is empty", current: null, visited: [], path: [] };
97+
return;
98+
}
99+
const visited = [];
100+
const path = [];
101+
102+
function* traverse(node) {
103+
if (!node) return;
104+
path.push(node.value);
105+
visited.push(node.value);
106+
yield {
107+
type: "visit",
108+
message: `Visiting ${node.value} (Preorder: Root → Left → Right)`,
109+
current: node.value, visited: [...visited], path: [...path], subtree: "root"
110+
};
111+
if (node.left) {
112+
yield {
113+
type: "traverse_left",
114+
message: `Moving to left subtree of ${node.value}`,
115+
current: node.value, visited: [...visited],path: [...path]
116+
};
117+
yield* traverse(node.left);
118+
}
119+
120+
if (node.right) {
121+
yield {
122+
type: "traverse_right",
123+
message: `Moving to right subtree of ${node.value}`,
124+
current: node.value, visited: [...visited], path: [...path] };
125+
yield* traverse(node.right);
126+
}
127+
path.pop();
128+
}
129+
130+
yield* traverse(root);
131+
132+
yield { type: "complete", message: `Preorder traversal complete: ${visited.join(" → ")}`, current: null, visited: visited, path: []};
133+
}
134+
135+
//postorder
136+
export function* postorderTraversal(root) {
137+
if (!root) {
138+
yield { type: "error", message: "Tree is empty", current: null, visited: [], path: [] };
139+
return;
140+
}
141+
const visited = [];
142+
const path = [];
143+
144+
function* traverse(node) {
145+
if (!node) return;
146+
path.push(node.value);
147+
if (node.left) {
148+
yield {
149+
type: "traverse_left",
150+
message: `Moving to left subtree of ${node.value}`,
151+
current: node.value,
152+
visited: [...visited],
153+
path: [...path]
154+
};
155+
yield* traverse(node.left);
156+
}
157+
158+
if (node.right) {
159+
yield {
160+
type: "traverse_right",
161+
message: `Moving to right subtree of ${node.value}`,
162+
current: node.value,
163+
visited: [...visited],
164+
path: [...path]
165+
};
166+
yield* traverse(node.right);
167+
}
168+
visited.push(node.value);
169+
yield {
170+
type: "visit",
171+
message: `Visiting ${node.value} (Postorder: Left → Right → Root)`,
172+
current: node.value,
173+
visited: [...visited],
174+
path: [...path],
175+
subtree: "root"
176+
};
177+
path.pop();
178+
}
179+
yield* traverse(root);
180+
yield {
181+
type: "complete",
182+
message: `Postorder traversal complete: ${visited.join(" → ")}`,
183+
current: null,
184+
visited: visited,
185+
path: []
186+
};
187+
}
188+
export function* treeTraversalGenerator(treeData, traversalType = "all") {
189+
const root = buildTreeFromLevelOrder(treeData);
190+
if (!root) {
191+
yield {
192+
type: "error",
193+
message: "Invalid tree data",
194+
tree: null,
195+
step: null
196+
};
197+
return;
198+
}
199+
if (traversalType === "all") {
200+
const traversals = [
201+
{ name: "inorder", generator: inorderTraversal },
202+
{ name: "preorder", generator: preorderTraversal },
203+
{ name: "postorder", generator: postorderTraversal }
204+
];
205+
const results = {
206+
inorder: { visited: [], steps: [] },
207+
preorder: { visited: [], steps: [] },
208+
postorder: { visited: [], steps: [] }
209+
};
210+
for (const { name, generator } of traversals) {
211+
for (const step of generator(cloneTree(root))) {
212+
results[name].steps.push(step);
213+
if (step.type === "complete") {
214+
results[name].visited = step.visited;
215+
}
216+
}
217+
}
218+
yield {
219+
type: "sync_step",
220+
tree: cloneTree(root),
221+
stepIndex: -1,
222+
inorder: { type: "start", message: "Starting Inorder traversal", visited: [], path: [] },
223+
preorder: { type: "start", message: "Starting Preorder traversal", visited: [], path: [] },
224+
postorder: { type: "start", message: "Starting Postorder traversal", visited: [], path: [] }
225+
};
226+
const maxSteps = Math.max(
227+
results.inorder.steps.length,
228+
results.preorder.steps.length,
229+
results.postorder.steps.length
230+
);
231+
for (let i = 0; i < maxSteps; i++) {
232+
const step = {
233+
type: "sync_step",
234+
tree: cloneTree(root),
235+
stepIndex: i,
236+
inorder: results.inorder.steps[i] || results.inorder.steps[results.inorder.steps.length - 1] || null,
237+
preorder: results.preorder.steps[i] || results.preorder.steps[results.preorder.steps.length - 1] || null,
238+
postorder: results.postorder.steps[i] || results.postorder.steps[results.postorder.steps.length - 1] || null
239+
};
240+
yield step;
241+
}
242+
yield {
243+
type: "complete",
244+
tree: cloneTree(root),
245+
inorder: results.inorder.visited,
246+
preorder: results.preorder.visited,
247+
postorder: results.postorder.visited,
248+
message: `All traversals complete. Inorder: [${results.inorder.visited.join(", ")}], Preorder: [${results.preorder.visited.join(", ")}], Postorder: [${results.postorder.visited.join(", ")}]`
249+
};
250+
} else {
251+
let generator;
252+
if (traversalType === "inorder") {
253+
generator = inorderTraversal;
254+
} else if (traversalType === "preorder") {
255+
generator = preorderTraversal;
256+
} else if (traversalType === "postorder") {
257+
generator = postorderTraversal;
258+
} else {
259+
yield {
260+
type: "error",
261+
message: "Invalid traversal type",
262+
tree: null,
263+
step: null
264+
};
265+
return;
266+
}
267+
for (const step of generator(cloneTree(root))) {
268+
yield {
269+
type: "single_step",
270+
tree: cloneTree(root),
271+
traversal: traversalType,
272+
step: step
273+
};
274+
}
275+
}
276+
}
277+
export { buildTreeFromLevelOrder, cloneTree, getAllNodeIds };
278+

0 commit comments

Comments
 (0)