Skip to content

Commit 4492a6f

Browse files
committed
draft: heap implementation
1 parent e9d9dd3 commit 4492a6f

File tree

5 files changed

+419
-0
lines changed

5 files changed

+419
-0
lines changed

src/app/components/algorithm-cards.jsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ const algorithms = [
2929
description: "Compare different recursive sorting algorithms",
3030
image: '/AlgorithmVisualizer/images/sort.png?height=200&width=300'
3131
},
32+
{
33+
id: 'heap',
34+
title: 'Heap Visualization',
35+
description: "Visualize heap data structure operations like insert and delete with max/min heap support",
36+
image: '/AlgorithmVisualizer/images/heap.png?height=200&width=300'
37+
},
3238
{
3339
id: 'n-queen',
3440
title: 'N Queen',

src/app/heap/canvasSVG.jsx

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { Component } from 'react';
2+
import TreeEdge from "../common/tree/TreeEdge";
3+
import TreeVertex from "../common/tree/TreeVertex";
4+
5+
class CanvasSvg extends Component {
6+
render() {
7+
let off = this.props.offset;
8+
9+
return (
10+
<div className="w-full h-full">
11+
<svg viewBox="0 0 240 150" xmlns="http://www.w3.org/2000/svg" className="w-full h-full">
12+
{this.props.edges.map((edge, index) => (
13+
<TreeEdge
14+
key={`edge-${index}`}
15+
id={index}
16+
pos={{
17+
x1: (edge.x1 - off) * 15 + 120,
18+
y1: edge.y1 * 15 + 10,
19+
x2: (edge.x2 - off) * 15 + 120,
20+
y2: edge.y2 * 15 + 10
21+
}}
22+
/>
23+
))}
24+
25+
{this.props.vertices.map((vertex, index) => (
26+
<TreeVertex
27+
key={`vertex-${index}`}
28+
id={index}
29+
current={this.props.current === index}
30+
label={vertex.label}
31+
ret={vertex.val}
32+
pos={{
33+
x: (vertex.x - off) * 15 + 120,
34+
y: vertex.y * 15 + 10,
35+
px: (vertex.px - off) * 15 + 120,
36+
py: vertex.py * 15 + 10
37+
}}
38+
/>
39+
))}
40+
</svg>
41+
</div>
42+
);
43+
}
44+
}
45+
46+
export default CanvasSvg;

src/app/heap/menu.jsx

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { CustomSelect } from '@/components/custom-select';
2+
import { Button } from '@/components/ui/button';
3+
import { Input } from '@/components/ui/input';
4+
// import { div } from '@/components/ui/div';
5+
import { Component } from 'react';
6+
7+
class Menu extends Component {
8+
handleInputChange = (event) => {
9+
const value = parseInt(event.target.value) || 0;
10+
this.props.setInputValue(value);
11+
}
12+
13+
handleHeapTypeChange = (index) => {
14+
this.props.setHeapType(index === 0); // 0 = Max Heap, 1 = Min Heap
15+
}
16+
17+
render() {
18+
return (
19+
<div className="w-64 bg-gray-100 p-4 space-y-6">
20+
<h2 className="text-lg font-semibold">Heap Controls</h2>
21+
22+
<CustomSelect
23+
title="Heap Type"
24+
options={['Max Heap', 'Min Heap']}
25+
onChange={this.handleHeapTypeChange}
26+
/>
27+
28+
<div className="space-y-2">
29+
<div htmlFor="value-input">Value to Insert</div>
30+
<Input
31+
id="value-input"
32+
type="number"
33+
value={this.props.inputValue}
34+
onChange={this.handleInputChange}
35+
placeholder="Enter value"
36+
disabled={this.props.disable}
37+
/>
38+
</div>
39+
40+
<div className="space-y-2">
41+
<div>Heap Info</div>
42+
<div className="text-sm text-gray-600">
43+
<p>Type: {this.props.isMaxHeap ? 'Max Heap' : 'Min Heap'}</p>
44+
<p>Size: {this.props.heapSize}</p>
45+
</div>
46+
</div>
47+
48+
<Button
49+
className="w-full"
50+
onClick={this.props.onPush}
51+
disabled={this.props.disable}
52+
style={this.isClickable()}
53+
>
54+
Push Value
55+
</Button>
56+
57+
<Button
58+
className="w-full"
59+
onClick={this.props.onPop}
60+
disabled={this.props.disable || this.props.heapSize === 0}
61+
style={this.isClickable()}
62+
>
63+
Pop Value
64+
</Button>
65+
66+
<Button
67+
className="w-full"
68+
onClick={this.props.onReset}
69+
disabled={this.props.disable}
70+
variant="outline"
71+
style={this.isClickable()}
72+
>
73+
Reset Heap
74+
</Button>
75+
</div>
76+
);
77+
}
78+
79+
isClickable = () => {
80+
if (this.props.disable) {
81+
return { cursor: "not-allowed" };
82+
} else {
83+
return {};
84+
}
85+
}
86+
}
87+
88+
export default Menu;

src/app/heap/page.jsx

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
"use client";
2+
3+
import Navbar from '@/components/navbar';
4+
import { Component } from 'react';
5+
import CanvasSvg from "./canvasSVG";
6+
import { Heap } from "@/lib/heap/Heap";
7+
import Menu from "./menu";
8+
9+
class HeapVisualization extends Component {
10+
constructor() {
11+
super();
12+
this.state = {
13+
heap: new Heap(true), // true for max heap
14+
vertices: [],
15+
edges: [],
16+
current: -1,
17+
isMaxHeap: true,
18+
inputValue: 0,
19+
offset: 0,
20+
animating: false
21+
}
22+
}
23+
24+
setHeapType = (isMaxHeap) => {
25+
this.setState({
26+
heap: new Heap(isMaxHeap),
27+
isMaxHeap,
28+
vertices: [],
29+
edges: [],
30+
offset: 0
31+
});
32+
}
33+
34+
setInputValue = (value) => {
35+
this.setState({ inputValue: value });
36+
}
37+
38+
pushValue = async () => {
39+
if (this.state.animating) return;
40+
41+
this.setState({ animating: true });
42+
const heap = this.state.heap;
43+
heap.push(this.state.inputValue);
44+
45+
await this.updateVisualization();
46+
this.setState({ animating: false });
47+
}
48+
49+
popValue = async () => {
50+
if (this.state.animating || this.state.heap.heap.length === 0) return;
51+
52+
this.setState({ animating: true });
53+
const heap = this.state.heap;
54+
heap.pop();
55+
56+
await this.updateVisualization();
57+
this.setState({ animating: false });
58+
}
59+
60+
reset = () => {
61+
this.setState({
62+
heap: new Heap(this.state.isMaxHeap),
63+
vertices: [],
64+
edges: [],
65+
offset: 0,
66+
current: -1
67+
});
68+
}
69+
70+
updateVisualization = async () => {
71+
const tree = this.state.heap.getVisualizationTree();
72+
73+
if (!tree) {
74+
this.setState({ vertices: [], edges: [], offset: 0 });
75+
return;
76+
}
77+
78+
this.setState({
79+
edges: [],
80+
vertices: [],
81+
offset: tree.x
82+
});
83+
84+
await this.buildVisualization(tree, undefined);
85+
}
86+
87+
buildVisualization = async (node, parent) => {
88+
let vertices = this.state.vertices;
89+
let current = vertices.length;
90+
91+
const vertexData = {
92+
label: node.tree.label,
93+
val: node.tree.node,
94+
x: node.x,
95+
y: node.y,
96+
px: parent ? parent.x : node.x,
97+
py: parent ? parent.y : node.y
98+
};
99+
100+
vertices.push(vertexData);
101+
this.setState({ vertices: [...vertices], current });
102+
103+
if (parent) {
104+
let edges = this.state.edges;
105+
edges.push({
106+
x1: parent.x,
107+
y1: parent.y,
108+
x2: node.x,
109+
y2: node.y
110+
});
111+
this.setState({ edges: [...edges] });
112+
}
113+
114+
await sleep(300);
115+
116+
for (let i = 0; i < node.children.length; i++) {
117+
await this.buildVisualization(node.children[i], node);
118+
}
119+
}
120+
121+
render() {
122+
return (
123+
<div className="flex flex-col h-screen">
124+
<Navbar title="Heap Visualization" />
125+
<div className="flex flex-1 overflow-hidden">
126+
<Menu
127+
isMaxHeap={this.state.isMaxHeap}
128+
inputValue={this.state.inputValue}
129+
heapSize={this.state.heap.heap.length}
130+
setHeapType={this.setHeapType}
131+
setInputValue={this.setInputValue}
132+
onPush={this.pushValue}
133+
onPop={this.popValue}
134+
onReset={this.reset}
135+
disable={this.state.animating}
136+
/>
137+
138+
<div className="flex flex-1 flex-col items-center justify-center overflow-auto">
139+
<div className="w-full h-full">
140+
<CanvasSvg
141+
vertices={this.state.vertices}
142+
edges={this.state.edges}
143+
current={this.state.current}
144+
offset={this.state.offset}
145+
/>
146+
</div>
147+
</div>
148+
</div>
149+
</div>
150+
);
151+
}
152+
}
153+
154+
function sleep(ms) {
155+
return new Promise(resolve => setTimeout(resolve, ms));
156+
}
157+
158+
export default HeapVisualization;

0 commit comments

Comments
 (0)