adding iterative traversal animations
This commit is contained in:
committed by
JamesFlare1212
parent
7b9d3983ed
commit
d0385b27f1
55
animations/trees/iterative/inorder/inorder.css
Normal file
55
animations/trees/iterative/inorder/inorder.css
Normal file
@@ -0,0 +1,55 @@
|
||||
body {
|
||||
margin: 0;
|
||||
background-color: #d3d3d3;
|
||||
font-family: Arial;
|
||||
}
|
||||
#main-container {
|
||||
display: flex;
|
||||
width: 90%;
|
||||
margin: 20px auto;
|
||||
}
|
||||
#tree-container {
|
||||
flex: 3;
|
||||
height: 600px;
|
||||
border: 2px solid #333;
|
||||
background-color: #d3d3d3;
|
||||
}
|
||||
#stack-container {
|
||||
flex: 1;
|
||||
height: 600px;
|
||||
border: 2px solid #333;
|
||||
background-color: #f0f0f0;
|
||||
margin-left: 10px;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#stack-title {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
#controls {
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
#log, #final-seq, #status {
|
||||
width: 90%;
|
||||
margin: 10px auto;
|
||||
padding: 10px;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
font-size: 16px;
|
||||
}
|
||||
#log {
|
||||
height: 100px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
font-size: 16px;
|
||||
margin: 0 10px;
|
||||
}
|
||||
.label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
30
animations/trees/iterative/inorder/inorder.html
Normal file
30
animations/trees/iterative/inorder/inorder.html
Normal file
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>In-Order Traversal (Step-by-Step)</title>
|
||||
<script src="konva.js"></script>
|
||||
<link rel="stylesheet" href="inorder.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="main-container">
|
||||
<div id="tree-container"></div>
|
||||
<div id="stack-container">
|
||||
<div id="stack-title">Stack</div>
|
||||
<div id="stack-visual"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="controls">
|
||||
<button id="stepBtn">Step</button>
|
||||
<button id="resetBtn">Start Over</button>
|
||||
</div>
|
||||
<div id="status">
|
||||
<span class="label">Now checking:</span> <span id="currentVal">None</span> |
|
||||
<span class="label">Status:</span> <span id="reason">Ready</span>
|
||||
</div>
|
||||
<div id="log"><strong>Traversal Sequence:</strong> <span id="seq"></span></div>
|
||||
<div id="final-seq"><strong>Final Sequence:</strong> 4 2 6 5 7 1 3 8 9</div>
|
||||
|
||||
<script src="inorder.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
266
animations/trees/iterative/inorder/inorder.js
Normal file
266
animations/trees/iterative/inorder/inorder.js
Normal file
@@ -0,0 +1,266 @@
|
||||
//set up the main canvas dimensions for tree visualization
|
||||
//using 75% of 90% window width to leave some margin
|
||||
const width = window.innerWidth * 0.9 * 0.75;
|
||||
const height = 600;
|
||||
|
||||
//create the main stage for tree visualization
|
||||
const stage = new Konva.Stage({
|
||||
container: 'tree-container', //html div id
|
||||
width: width,
|
||||
height: height
|
||||
});
|
||||
|
||||
//create and add a layer to the stage for drawing
|
||||
const layer = new Konva.Layer();
|
||||
stage.add(layer);
|
||||
|
||||
//manually calculated x,y coordinates for visual balance
|
||||
const positions = {
|
||||
1: { x: width / 2, y: 80 }, //root node centered
|
||||
2: { x: width / 2 - 200, y: 180 }, //left child
|
||||
3: { x: width / 2 + 200, y: 180 }, //right child
|
||||
4: { x: width / 2 - 300, y: 280 }, //left-left grandchild
|
||||
5: { x: width / 2 - 100, y: 280 }, //left-right grandchild
|
||||
6: { x: width / 2 - 150, y: 380 }, //left-right-left great-grandchild
|
||||
7: { x: width / 2 - 50, y: 380 }, //left-right-right great-grandchild
|
||||
8: { x: width / 2 + 300, y: 280 }, //right-right grandchild
|
||||
9: { x: width / 2 + 350, y: 380 } //right-right-right great-grandchild
|
||||
};
|
||||
|
||||
//binary tree structure definition--------------------------------------
|
||||
const tree = {
|
||||
val: 1, //root node
|
||||
left: {
|
||||
val: 2,
|
||||
left: { val: 4, left: null, right: null }, //leaf node
|
||||
right: {
|
||||
val: 5,
|
||||
left: { val: 6, left: null, right: null }, //leaf node
|
||||
right: { val: 7, left: null, right: null } //leaf node
|
||||
}
|
||||
},
|
||||
right: {
|
||||
val: 3,
|
||||
left: null, //no left child
|
||||
right: {
|
||||
val: 8,
|
||||
left: null, //no left child
|
||||
right: { val: 9, left: null, right: null } //leaf node
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//store references to all visual node elements
|
||||
const nodes = {};
|
||||
|
||||
/**
|
||||
* recursively draws the tree starting from given node
|
||||
*/
|
||||
function drawTree(node, parent = null) {
|
||||
if (!node) return; //base case
|
||||
|
||||
//get pre-defined position for this node
|
||||
const pos = positions[node.val];
|
||||
|
||||
//if not root node, draw line to parent
|
||||
if (parent) {
|
||||
const parentPos = positions[parent.val];
|
||||
layer.add(new Konva.Line({
|
||||
points: [parentPos.x, parentPos.y, pos.x, pos.y],
|
||||
stroke: 'black',
|
||||
strokeWidth: 2
|
||||
}));
|
||||
}
|
||||
|
||||
//create circle for node
|
||||
const circle = new Konva.Circle({
|
||||
x: pos.x,
|
||||
y: pos.y,
|
||||
radius: 20,
|
||||
fill: 'white',
|
||||
stroke: 'black',
|
||||
strokeWidth: 2
|
||||
});
|
||||
|
||||
//create text label for node value
|
||||
const text = new Konva.Text({
|
||||
x: pos.x - 5, //adjust slightly for centering
|
||||
y: pos.y - 8,
|
||||
text: node.val.toString(),
|
||||
fontSize: 16,
|
||||
fill: 'black'
|
||||
});
|
||||
|
||||
//add elements to layer
|
||||
layer.add(circle);
|
||||
layer.add(text);
|
||||
|
||||
//store references for later manipulation
|
||||
nodes[node.val] = { circle, text };
|
||||
|
||||
//recursively draw left and right children
|
||||
drawTree(node.left, node);
|
||||
drawTree(node.right, node);
|
||||
}
|
||||
|
||||
//create a separate stage for stack visualization
|
||||
const stackStage = new Konva.Stage({
|
||||
container: 'stack-visual', //html div id
|
||||
width: 200, //narrower since it's just for stack
|
||||
height: 500 //tall enough to show several stack items
|
||||
});
|
||||
|
||||
//create and add layer for stack visualization
|
||||
const stackLayer = new Konva.Layer();
|
||||
stackStage.add(stackLayer);
|
||||
|
||||
//visual elements for stack representation
|
||||
let stackVisual = [];
|
||||
|
||||
/**
|
||||
* visualizes pushing a value onto the stack
|
||||
*/
|
||||
function pushStack(val) {
|
||||
//create rectangle for stack element
|
||||
const box = new Konva.Rect({
|
||||
x: 50,
|
||||
y: 400 - stackVisual.length * 30, //stack grows upward
|
||||
width: 40,
|
||||
height: 25,
|
||||
fill: '#89CFF0', //light blue color
|
||||
stroke: 'black'
|
||||
});
|
||||
|
||||
//create text label for stack element
|
||||
const label = new Konva.Text({
|
||||
x: 60,
|
||||
y: 405 - stackVisual.length * 30,
|
||||
text: val.toString(),
|
||||
fontSize: 16,
|
||||
fill: 'black'
|
||||
});
|
||||
|
||||
//add to stack layer
|
||||
stackLayer.add(box);
|
||||
stackLayer.add(label);
|
||||
|
||||
stackVisual.push({ box, label }); //store reference
|
||||
stackLayer.draw(); //redraw stack layer
|
||||
}
|
||||
|
||||
/**
|
||||
* visualizes popping from the stack
|
||||
*/
|
||||
function popStack() {
|
||||
const item = stackVisual.pop();
|
||||
if (item) {
|
||||
//remove visual elements
|
||||
item.box.destroy();
|
||||
item.label.destroy();
|
||||
stackLayer.draw();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* highlights a node with specified color
|
||||
*/
|
||||
function highlight(val, color = 'yellow') {
|
||||
const node = nodes[val];
|
||||
if (node) {
|
||||
node.circle.fill(color);
|
||||
layer.draw(); //redraw to show changes
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* resets all node highlights to white
|
||||
*/
|
||||
function resetHighlights() {
|
||||
for (let val in nodes) {
|
||||
nodes[val].circle.fill('white');
|
||||
}
|
||||
layer.draw();
|
||||
}
|
||||
|
||||
/**
|
||||
* updates the status display
|
||||
*/
|
||||
function updateStatus(current, reason) {
|
||||
document.getElementById('currentVal').innerText = current ?? 'None';
|
||||
document.getElementById('reason').innerText = reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* logs a visited node to the sequence display
|
||||
*/
|
||||
function logVisit(val) {
|
||||
const seq = document.getElementById('seq');
|
||||
seq.innerText += ` ${val}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* resets the entire visualization to initial state
|
||||
*/
|
||||
function resetAll() {
|
||||
//clear stack visualization
|
||||
while (stackVisual.length > 0) {
|
||||
popStack();
|
||||
}
|
||||
|
||||
//reset traversal state
|
||||
stack.length = 0;
|
||||
current = tree;
|
||||
stepReady = true;
|
||||
|
||||
//clear displays
|
||||
document.getElementById('seq').innerText = '';
|
||||
updateStatus('None', 'Ready');
|
||||
resetHighlights();
|
||||
}
|
||||
|
||||
//traversal state variables
|
||||
let current = tree; //current node being processed
|
||||
const stack = []; //stack used for traversal
|
||||
let stepReady = true; //flag to control step-by-step execution
|
||||
|
||||
//handler for step button - performs one step of inorder traversal
|
||||
document.getElementById("stepBtn").onclick = () => {
|
||||
if (!stepReady) return; //don't allow overlapping steps
|
||||
stepReady = false;
|
||||
|
||||
if (current) {
|
||||
//step 1: go as far left as possible
|
||||
updateStatus(current.val, 'go left');
|
||||
stack.push(current);
|
||||
pushStack(current.val);
|
||||
current = current.left;
|
||||
stepReady = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (stack.length > 0) {
|
||||
//step 2: visit node (inorder)
|
||||
current = stack.pop();
|
||||
updateStatus(current.val, 'visit node');
|
||||
popStack();
|
||||
highlight(current.val);
|
||||
logVisit(current.val);
|
||||
|
||||
//step 3: move to right subtree
|
||||
setTimeout(() => {
|
||||
highlight(current.val, 'white');
|
||||
current = current.right;
|
||||
stepReady = true;
|
||||
}, 500);
|
||||
} else {
|
||||
//traversal complete
|
||||
updateStatus('None', 'done!');
|
||||
}
|
||||
};
|
||||
|
||||
//event handler for reset button
|
||||
document.getElementById("resetBtn").onclick = resetAll;
|
||||
|
||||
//initial render of the tree
|
||||
drawTree(tree);
|
||||
layer.draw();
|
||||
18223
animations/trees/iterative/inorder/konva.js
Normal file
18223
animations/trees/iterative/inorder/konva.js
Normal file
File diff suppressed because it is too large
Load Diff
18223
animations/trees/iterative/postorder/konva.js
Normal file
18223
animations/trees/iterative/postorder/konva.js
Normal file
File diff suppressed because it is too large
Load Diff
54
animations/trees/iterative/postorder/postorder.css
Normal file
54
animations/trees/iterative/postorder/postorder.css
Normal file
@@ -0,0 +1,54 @@
|
||||
body {
|
||||
margin: 0;
|
||||
background-color: #d3d3d3;
|
||||
font-family: Arial;
|
||||
}
|
||||
#main-container {
|
||||
display: flex;
|
||||
width: 90%;
|
||||
margin: 20px auto;
|
||||
}
|
||||
#tree-container {
|
||||
flex: 3;
|
||||
height: 600px;
|
||||
border: 2px solid #333;
|
||||
background-color: #d3d3d3;
|
||||
}
|
||||
#stack-container {
|
||||
flex: 1;
|
||||
height: 600px;
|
||||
border: 2px solid #333;
|
||||
background-color: #f0f0f0;
|
||||
margin-left: 10px;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#stack-title {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
#controls {
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
#log, #final-seq, #status {
|
||||
width: 90%;
|
||||
margin: 10px auto;
|
||||
padding: 10px;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
font-size: 16px;
|
||||
}
|
||||
#log {
|
||||
height: 100px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
font-size: 16px;
|
||||
margin: 0 10px;
|
||||
}
|
||||
.label {
|
||||
font-weight: bold;
|
||||
}
|
||||
30
animations/trees/iterative/postorder/postorder.html
Normal file
30
animations/trees/iterative/postorder/postorder.html
Normal file
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Post-Order Traversal (Step-by-Step)</title>
|
||||
<script src="konva.js"></script>
|
||||
<link rel="stylesheet" href="postorder.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="main-container">
|
||||
<div id="tree-container"></div>
|
||||
<div id="stack-container">
|
||||
<div id="stack-title">Stack</div>
|
||||
<div id="stack-visual"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="controls">
|
||||
<button id="stepBtn">Step</button>
|
||||
<button id="resetBtn">Start Over</button>
|
||||
</div>
|
||||
<div id="status">
|
||||
<span class="label">Now checking:</span> <span id="currentVal">None</span> |
|
||||
<span class="label">Status:</span> <span id="reason">Ready</span>
|
||||
</div>
|
||||
<div id="log"><strong>Traversal Sequence:</strong> <span id="seq"></span></div>
|
||||
<div id="final-seq"><strong>Final Sequence:</strong> 4 6 7 5 2 9 8 3 1</div>
|
||||
|
||||
<script src="postorder.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
283
animations/trees/iterative/postorder/postorder.js
Normal file
283
animations/trees/iterative/postorder/postorder.js
Normal file
@@ -0,0 +1,283 @@
|
||||
//set up the main canvas dimensions for tree visualization
|
||||
//using 75% of 90% window width to leave some margin
|
||||
const width = window.innerWidth * 0.9 * 0.75;
|
||||
const height = 600;
|
||||
|
||||
//create the main stage for tree visualization
|
||||
const stage = new Konva.Stage({
|
||||
container: 'tree-container', //HTML div id
|
||||
width: width,
|
||||
height: height
|
||||
});
|
||||
|
||||
//create and add a layer to the stage for drawing
|
||||
let layer = new Konva.Layer();
|
||||
stage.add(layer);
|
||||
|
||||
//create a separate stage for stack visualization
|
||||
const stackStage = new Konva.Stage({
|
||||
container: 'stack-visual', //HTML div id
|
||||
width: 200, //narrower since it's just for stack
|
||||
height: 500 //tall enough to show several stack items
|
||||
});
|
||||
|
||||
//create and add layer for stack visualization
|
||||
let stackLayer = new Konva.Layer();
|
||||
stackStage.add(stackLayer);
|
||||
|
||||
//manually calculated x,y coordinates for visual balance
|
||||
const positions = {
|
||||
1: { x: width / 2, y: 80 }, //root node centered
|
||||
2: { x: width / 2 - 200, y: 180 }, //left child
|
||||
3: { x: width / 2 + 200, y: 180 }, //right child
|
||||
4: { x: width / 2 - 300, y: 280 }, //left-left grandchild
|
||||
5: { x: width / 2 - 100, y: 280 }, //left-right grandchild
|
||||
6: { x: width / 2 - 150, y: 380 }, //left-right-left great-grandchild
|
||||
7: { x: width / 2 - 50, y: 380 }, //left-right-right great-grandchild
|
||||
8: { x: width / 2 + 300, y: 280 }, //right-right grandchild
|
||||
9: { x: width / 2 + 350, y: 380 } //right-right-right great-grandchild
|
||||
};
|
||||
|
||||
let nodes = {};//store references to all visual node elements
|
||||
let stackVisual = []; //visual elements for stack rep
|
||||
let stack = []; //stack used for traversal
|
||||
let current = null;// Current node being processe
|
||||
let lastVisited = null; //track last visited node for postorder logic
|
||||
let stepReady = true; //flag to control step-by-step execution
|
||||
|
||||
//binary tree structure definition--------------------------------------
|
||||
const treeData = {
|
||||
val: 1,//root node
|
||||
left: {
|
||||
val: 2,
|
||||
left: { val: 4, left: null, right: null },//leaf node
|
||||
right: {
|
||||
val: 5,
|
||||
left: { val: 6, left: null, right: null },//leaf node
|
||||
right: { val: 7, left: null, right: null }//leaf node
|
||||
}
|
||||
},
|
||||
right: {
|
||||
val: 3,
|
||||
left: null,//no left child
|
||||
right: {
|
||||
val: 8,
|
||||
left: null,//no left child
|
||||
right: { val: 9, left: null, right: null }//leaf node
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* recursively draws the tree starting from given node
|
||||
*/
|
||||
function drawTree(node, parent = null) {
|
||||
if (!node) return; // Base case
|
||||
|
||||
//get pre-defined position for this node
|
||||
const pos = positions[node.val];
|
||||
|
||||
//if not root node, draw line to parent
|
||||
if (parent) {
|
||||
const parentPos = positions[parent.val];
|
||||
layer.add(new Konva.Line({
|
||||
points: [parentPos.x, parentPos.y, pos.x, pos.y],
|
||||
stroke: 'black',
|
||||
strokeWidth: 2
|
||||
}));
|
||||
}
|
||||
|
||||
//create circle for node
|
||||
const circle = new Konva.Circle({
|
||||
x: pos.x,
|
||||
y: pos.y,
|
||||
radius: 20,
|
||||
fill: 'white',
|
||||
stroke: 'black',
|
||||
strokeWidth: 2
|
||||
});
|
||||
|
||||
//create text label for node value
|
||||
const text = new Konva.Text({
|
||||
x: pos.x - 5, //adjust slightly for centering
|
||||
y: pos.y - 8,
|
||||
text: node.val.toString(),
|
||||
fontSize: 16,
|
||||
fill: 'black'
|
||||
});
|
||||
|
||||
//add elements to layer
|
||||
layer.add(circle);
|
||||
layer.add(text);
|
||||
|
||||
//store references for later manipulation
|
||||
nodes[node.val] = { circle, text };
|
||||
|
||||
//recursively draw left and right children
|
||||
drawTree(node.left, node);
|
||||
drawTree(node.right, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* visualizes pushing a value onto the stack
|
||||
*/
|
||||
function pushStack(val) {
|
||||
//create rectangle for stack element
|
||||
const box = new Konva.Rect({
|
||||
x: 50,
|
||||
y: 400 - stackVisual.length * 30, //stack grows upward
|
||||
width: 40,
|
||||
height: 25,
|
||||
fill: '#89CFF0', //light blue color
|
||||
stroke: 'black'
|
||||
});
|
||||
|
||||
//create text label for stack element
|
||||
const label = new Konva.Text({
|
||||
x: 60,
|
||||
y: 405 - stackVisual.length * 30,
|
||||
text: val.toString(),
|
||||
fontSize: 16,
|
||||
fill: 'black'
|
||||
});
|
||||
|
||||
// Add to stack layer
|
||||
stackLayer.add(box);
|
||||
stackLayer.add(label);
|
||||
|
||||
stackVisual.push({ box, label });//store reference
|
||||
stackLayer.draw();//redraw stack layer
|
||||
}
|
||||
|
||||
/**
|
||||
* visualizes popping from the stack
|
||||
*/
|
||||
function popStack() {
|
||||
const item = stackVisual.pop();
|
||||
if (item) {
|
||||
//remove visual elements
|
||||
item.box.destroy();
|
||||
item.label.destroy();
|
||||
stackLayer.draw();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* highlights a node with specified color
|
||||
*/
|
||||
function highlight(val, color = 'yellow') {
|
||||
const node = nodes[val];
|
||||
if (node) {
|
||||
node.circle.fill(color);
|
||||
layer.draw(); //redraw to show changes
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* resets all node highlights to white
|
||||
*/
|
||||
function resetHighlights() {
|
||||
for (let val in nodes) {
|
||||
nodes[val].circle.fill('white');
|
||||
}
|
||||
layer.draw();
|
||||
}
|
||||
|
||||
/**
|
||||
* updates the status display
|
||||
*/
|
||||
function updateStatus(current, reason) {
|
||||
document.getElementById('currentVal').innerText = current ?? 'None';
|
||||
document.getElementById('reason').innerText = reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* logs a visited node to the sequence display
|
||||
*/
|
||||
function logVisit(val) {
|
||||
const seq = document.getElementById('seq');
|
||||
seq.innerText += ` ${val}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* resets the entire visualization to initial state
|
||||
*/
|
||||
function resetAll() {
|
||||
//clear existing layers
|
||||
layer.destroy();
|
||||
stackLayer.destroy();
|
||||
|
||||
//create new layers
|
||||
layer = new Konva.Layer();
|
||||
stackLayer = new Konva.Layer();
|
||||
|
||||
//add layers to stages
|
||||
stage.add(layer);
|
||||
stackStage.add(stackLayer);
|
||||
|
||||
//reset all variables
|
||||
nodes = {};
|
||||
stackVisual = [];
|
||||
stack = [];
|
||||
current = treeData;//start at root
|
||||
lastVisited = null;
|
||||
stepReady = true;
|
||||
|
||||
//clear displays
|
||||
document.getElementById('seq').innerText = '';
|
||||
updateStatus('None', 'Ready');
|
||||
|
||||
//redraw tree
|
||||
drawTree(treeData);
|
||||
layer.draw();
|
||||
}
|
||||
|
||||
//handler for step button - performs one step of postorder traversal
|
||||
document.getElementById("stepBtn").onclick = () => {
|
||||
if (!stepReady) return; //don't allow overlapping steps
|
||||
stepReady = false;
|
||||
|
||||
if (current) {
|
||||
//step 1: Go as far left as possible
|
||||
updateStatus(current.val, 'Going left');
|
||||
stack.push(current);
|
||||
pushStack(current.val);
|
||||
current = current.left;
|
||||
stepReady = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (stack.length > 0) {
|
||||
const peekNode = stack[stack.length - 1];//look at top of stack
|
||||
|
||||
if (peekNode.right && lastVisited !== peekNode.right) {
|
||||
//step 2: If right exists and not visited, go right
|
||||
updateStatus(peekNode.val, 'Going right');
|
||||
current = peekNode.right; //move to right child
|
||||
stepReady = true;
|
||||
} else {
|
||||
//step 3: Visit the node (left and right done)
|
||||
const node = stack.pop();
|
||||
popStack(); //remove from visual stack
|
||||
updateStatus(node.val, 'Visiting node');
|
||||
highlight(node.val); //visually mark node
|
||||
logVisit(node.val); //add to sequence
|
||||
lastVisited = node; //remember we visited this
|
||||
|
||||
//reset highlight after delay
|
||||
setTimeout(() => {
|
||||
highlight(node.val, 'white');
|
||||
stepReady = true;
|
||||
}, 500);
|
||||
}
|
||||
} else {
|
||||
//traversal complete
|
||||
updateStatus('None', 'Done!');
|
||||
}
|
||||
};
|
||||
|
||||
//event handler for reset button
|
||||
document.getElementById("resetBtn").onclick = resetAll;
|
||||
|
||||
//initialize visualization
|
||||
resetAll();
|
||||
18223
animations/trees/iterative/preorder/konva.js
Normal file
18223
animations/trees/iterative/preorder/konva.js
Normal file
File diff suppressed because it is too large
Load Diff
54
animations/trees/iterative/preorder/preorder.css
Normal file
54
animations/trees/iterative/preorder/preorder.css
Normal file
@@ -0,0 +1,54 @@
|
||||
body {
|
||||
margin: 0;
|
||||
background-color: #d3d3d3;
|
||||
font-family: Arial;
|
||||
}
|
||||
#main-container {
|
||||
display: flex;
|
||||
width: 90%;
|
||||
margin: 20px auto;
|
||||
}
|
||||
#tree-container {
|
||||
flex: 3;
|
||||
height: 600px;
|
||||
border: 2px solid #333;
|
||||
background-color: #d3d3d3;
|
||||
}
|
||||
#stack-container {
|
||||
flex: 1;
|
||||
height: 600px;
|
||||
border: 2px solid #333;
|
||||
background-color: #f0f0f0;
|
||||
margin-left: 10px;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#stack-title {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
#controls {
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
#log, #final-seq, #status {
|
||||
width: 90%;
|
||||
margin: 10px auto;
|
||||
padding: 10px;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
font-size: 16px;
|
||||
}
|
||||
#log {
|
||||
height: 100px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
font-size: 16px;
|
||||
margin: 0 10px;
|
||||
}
|
||||
.label {
|
||||
font-weight: bold;
|
||||
}
|
||||
30
animations/trees/iterative/preorder/preorder.html
Normal file
30
animations/trees/iterative/preorder/preorder.html
Normal file
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Pre-Order Traversal (Step-by-Step)</title>
|
||||
<script src="konva.js"></script>
|
||||
<link rel="stylesheet" href="preorder.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="main-container">
|
||||
<div id="tree-container"></div>
|
||||
<div id="stack-container">
|
||||
<div id="stack-title">Stack</div>
|
||||
<div id="stack-visual"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="controls">
|
||||
<button id="stepBtn">Step</button>
|
||||
<button id="resetBtn">Start Over</button>
|
||||
</div>
|
||||
<div id="status">
|
||||
<span class="label">Now checking:</span> <span id="currentVal">None</span> |
|
||||
<span class="label">Status:</span> <span id="reason">Ready</span>
|
||||
</div>
|
||||
<div id="log"><strong>Traversal Sequence:</strong> <span id="seq"></span></div>
|
||||
<div id="final-seq"><strong>Final Sequence:</strong> 1 2 4 5 6 7 3 8 9</div>
|
||||
|
||||
<script src="preorder.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
278
animations/trees/iterative/preorder/preorder.js
Normal file
278
animations/trees/iterative/preorder/preorder.js
Normal file
@@ -0,0 +1,278 @@
|
||||
//set up the main canvas dimensions for tree visualization
|
||||
//using 75% of 90% window width to leave some margin
|
||||
const width = window.innerWidth * 0.9 * 0.75;
|
||||
const height = 600;
|
||||
|
||||
//create the main stage for tree visualization
|
||||
const stage = new Konva.Stage({
|
||||
container: 'tree-container', //html div id
|
||||
width: width,
|
||||
height: height
|
||||
});
|
||||
|
||||
//create and add a layer to the stage for drawing
|
||||
let layer = new Konva.Layer();
|
||||
stage.add(layer);
|
||||
|
||||
//create a separate stage for stack visualization
|
||||
const stackStage = new Konva.Stage({
|
||||
container: 'stack-visual', //html div id
|
||||
width: 200, //narrower since it's just for stack
|
||||
height: 500 //tall enough to show several stack items
|
||||
});
|
||||
|
||||
//create and add layer for stack visualization
|
||||
let stackLayer = new Konva.Layer();
|
||||
stackStage.add(stackLayer);
|
||||
|
||||
//manually calculated x,y coordinates for visual balance
|
||||
const positions = {
|
||||
1: { x: width / 2, y: 80 }, //root node centered
|
||||
2: { x: width / 2 - 200, y: 180 }, //left child
|
||||
3: { x: width / 2 + 200, y: 180 }, //right child
|
||||
4: { x: width / 2 - 300, y: 280 }, //left-left grandchild
|
||||
5: { x: width / 2 - 100, y: 280 }, //left-right grandchild
|
||||
6: { x: width / 2 - 150, y: 380 }, //left-right-left great-grandchild
|
||||
7: { x: width / 2 - 50, y: 380 }, //left-right-right great-grandchild
|
||||
8: { x: width / 2 + 300, y: 280 }, //right-right grandchild
|
||||
9: { x: width / 2 + 350, y: 380 } //right-right-right great-grandchild
|
||||
};
|
||||
|
||||
let nodes = {}; //store references to all visual node elements
|
||||
let stackVisual = []; //visual elements for stack representation
|
||||
let stack = []; //stack used for traversal
|
||||
let current = null; //current node being processed
|
||||
let stepReady = true; //flag to control step-by-step execution
|
||||
|
||||
//binary tree structure definition--------------------------------------
|
||||
const treeData = {
|
||||
val: 1, //root node
|
||||
left: {
|
||||
val: 2,
|
||||
left: { val: 4, left: null, right: null }, //leaf node
|
||||
right: {
|
||||
val: 5,
|
||||
left: { val: 6, left: null, right: null }, //leaf node
|
||||
right: { val: 7, left: null, right: null } //leaf node
|
||||
}
|
||||
},
|
||||
right: {
|
||||
val: 3,
|
||||
left: null, //no left child
|
||||
right: {
|
||||
val: 8,
|
||||
left: null, //no left child
|
||||
right: { val: 9, left: null, right: null } //leaf node
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* recursively draws the tree starting from given node
|
||||
*/
|
||||
function drawTree(node, parent = null) {
|
||||
if (!node) return; //base case
|
||||
|
||||
//get pre-defined position for this node
|
||||
const pos = positions[node.val];
|
||||
|
||||
//if not root node, draw line to parent
|
||||
if (parent) {
|
||||
const parentPos = positions[parent.val];
|
||||
layer.add(new Konva.Line({
|
||||
points: [parentPos.x, parentPos.y, pos.x, pos.y],
|
||||
stroke: 'black',
|
||||
strokeWidth: 2
|
||||
}));
|
||||
}
|
||||
|
||||
//create circle for node
|
||||
const circle = new Konva.Circle({
|
||||
x: pos.x,
|
||||
y: pos.y,
|
||||
radius: 20,
|
||||
fill: 'white',
|
||||
stroke: 'black',
|
||||
strokeWidth: 2
|
||||
});
|
||||
|
||||
//create text label for node value
|
||||
const text = new Konva.Text({
|
||||
x: pos.x - 5, //adjust slightly for centering
|
||||
y: pos.y - 8,
|
||||
text: node.val.toString(),
|
||||
fontSize: 16,
|
||||
fill: 'black'
|
||||
});
|
||||
|
||||
//add elements to layer
|
||||
layer.add(circle);
|
||||
layer.add(text);
|
||||
|
||||
//store references for later manipulation
|
||||
nodes[node.val] = { circle, text };
|
||||
|
||||
//recursively draw left and right children
|
||||
drawTree(node.left, node);
|
||||
drawTree(node.right, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* visualizes pushing a value onto the stack
|
||||
*/
|
||||
function pushStack(val) {
|
||||
//create rectangle for stack element
|
||||
const box = new Konva.Rect({
|
||||
x: 50,
|
||||
y: 400 - stackVisual.length * 30, //stack grows upward
|
||||
width: 40,
|
||||
height: 25,
|
||||
fill: '#89CFF0', //light blue color
|
||||
stroke: 'black'
|
||||
});
|
||||
|
||||
//create text label for stack element
|
||||
const label = new Konva.Text({
|
||||
x: 60,
|
||||
y: 405 - stackVisual.length * 30,
|
||||
text: val.toString(),
|
||||
fontSize: 16,
|
||||
fill: 'black'
|
||||
});
|
||||
|
||||
//add to stack layer
|
||||
stackLayer.add(box);
|
||||
stackLayer.add(label);
|
||||
|
||||
stackVisual.push({ box, label });//store reference
|
||||
stackLayer.draw(); //redraw stack layer
|
||||
}
|
||||
|
||||
/**
|
||||
* visualizes popping from the stack
|
||||
*/
|
||||
function popStack() {
|
||||
const item = stackVisual.pop();
|
||||
if (item) {
|
||||
//remove visual elements
|
||||
item.box.destroy();
|
||||
item.label.destroy();
|
||||
stackLayer.draw();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* highlights a node with specified color
|
||||
*/
|
||||
function highlight(val, color = 'yellow') {
|
||||
const node = nodes[val];
|
||||
if (node) {
|
||||
node.circle.fill(color);
|
||||
layer.draw();//redraw to show changes
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* resets all node highlights to white
|
||||
*/
|
||||
function resetHighlights() {
|
||||
for (let val in nodes) {
|
||||
nodes[val].circle.fill('white');
|
||||
}
|
||||
layer.draw();
|
||||
}
|
||||
|
||||
/**
|
||||
* updates the status display
|
||||
*/
|
||||
function updateStatus(current, reason) {
|
||||
document.getElementById('currentVal').innerText = current ?? 'None';
|
||||
document.getElementById('reason').innerText = reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* logs a visited node to the sequence display
|
||||
*/
|
||||
function logVisit(val) {
|
||||
const seq = document.getElementById('seq');
|
||||
seq.innerText += ` ${val}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* resets the entire visualization to initial state
|
||||
*/
|
||||
function resetAll() {
|
||||
//clear existing layers
|
||||
layer.destroy();
|
||||
stackLayer.destroy();
|
||||
|
||||
//create new layers
|
||||
layer = new Konva.Layer();
|
||||
stackLayer = new Konva.Layer();
|
||||
|
||||
//add layers to stages
|
||||
stage.add(layer);
|
||||
stackStage.add(stackLayer);
|
||||
|
||||
//reset all variables
|
||||
nodes = {};
|
||||
stackVisual = [];
|
||||
stack = [];
|
||||
current = treeData; //start at root
|
||||
stepReady = true;
|
||||
|
||||
//clear displays
|
||||
document.getElementById('seq').innerText = '';
|
||||
updateStatus('None', 'Ready');
|
||||
|
||||
//redraw tree
|
||||
drawTree(treeData);
|
||||
layer.draw();
|
||||
}
|
||||
|
||||
//handler for step button - performs one step of preorder traversal
|
||||
document.getElementById("stepBtn").onclick = () => {
|
||||
if (!stepReady) return;//don't allow overlapping steps
|
||||
stepReady = false;
|
||||
|
||||
if (current) {
|
||||
//step 1: visit node first (preorder)
|
||||
updateStatus(current.val, 'visiting node');
|
||||
highlight(current.val);
|
||||
logVisit(current.val);
|
||||
|
||||
//push right child first so left is processed first
|
||||
if (current.right) {
|
||||
stack.push(current.right);
|
||||
pushStack(current.right.val);
|
||||
}
|
||||
|
||||
//move to left child
|
||||
const prevcurrent = current.val;
|
||||
current = current.left;
|
||||
|
||||
//reset highlight after delay
|
||||
setTimeout(() => {
|
||||
highlight(prevcurrent, 'white');
|
||||
stepReady = true;
|
||||
}, 500);
|
||||
return;
|
||||
}
|
||||
|
||||
if (stack.length > 0) {
|
||||
//step 2: process nodes from stack
|
||||
current = stack.pop();
|
||||
popStack();
|
||||
updateStatus(current.val, 'popped from stack');
|
||||
stepReady = true;
|
||||
} else {
|
||||
//traversal complete
|
||||
updateStatus('None', 'done!');
|
||||
}
|
||||
};
|
||||
|
||||
//event handler for reset button
|
||||
document.getElementById("resetBtn").onclick = resetAll;
|
||||
|
||||
//initialize visualization
|
||||
resetAll();
|
||||
Reference in New Issue
Block a user