Skip to content

Commit a82a9b7

Browse files
bellman ford algorithm
1 parent ca18504 commit a82a9b7

File tree

7 files changed

+632
-2
lines changed

7 files changed

+632
-2
lines changed

src/App.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ import React from "react";
22
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
33
import Homepage from "./pages/Homepage";
44
import SortingPage from "./pages/sorting/SortingPage";
5+
import GraphPage from "./pages/graph/GraphPage";
56

67
function App() {
78
return (
89
<Router>
910
<Routes>
1011
<Route path="/" element={<Homepage />} />
1112
<Route path="/sorting" element={<SortingPage />} />
13+
<Route path="/graph" element={<GraphPage />} />
1214
</Routes>
1315
</Router>
1416
);
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
export default function runBellmanFord(nodes, edges, source) {
2+
const dist = {};
3+
nodes.forEach(n => dist[n] = Infinity);
4+
dist[source] = 0;
5+
6+
const steps = [];
7+
8+
// Relax edges |V|-1 times
9+
for (let i = 0; i < nodes.length - 1; i++) {
10+
for (const edge of edges) {
11+
const { from, to, weight } = edge;
12+
const step = {
13+
type: "relax",
14+
iteration: i + 1,
15+
edge,
16+
prevDistance: dist[to]
17+
};
18+
19+
if (dist[from] + weight < dist[to]) {
20+
dist[to] = dist[from] + weight;
21+
step.updatedDistance = dist[to];
22+
}
23+
24+
steps.push(step);
25+
}
26+
}
27+
28+
// Check for negative weight cycles
29+
for (const edge of edges) {
30+
const { from, to, weight } = edge;
31+
if (dist[from] + weight < dist[to]) {
32+
steps.push({ type: "negativeCycle", edge });
33+
}
34+
}
35+
36+
return steps;
37+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import React from "react";
2+
3+
export default function BellmanFordVisualizer({ nodes, edges, distances, highlight }) {
4+
return (
5+
<div className="text-white flex flex-col items-center gap-6 mt-8">
6+
{/* Nodes */}
7+
<div className="grid grid-cols-6 gap-4">
8+
{nodes.map((n) => (
9+
<div
10+
key={n}
11+
className={`relative flex flex-col items-center justify-center w-20 h-20 rounded-full border-4 text-xl font-bold transition ${
12+
highlight?.dist && highlight.dist[n] !== undefined
13+
? "border-emerald-400 bg-gray-800"
14+
: "border-gray-600 bg-gray-900"
15+
}`}
16+
>
17+
{n}
18+
<div className="text-xs mt-1 text-gray-300">
19+
{distances[n] === Infinity ? "∞" : distances[n] ?? "∞"}
20+
</div>
21+
</div>
22+
))}
23+
</div>
24+
25+
{/* Edges */}
26+
<div className="bg-gray-800 p-4 rounded-lg shadow-lg w-96">
27+
<h2 className="text-lg font-semibold mb-2">Edges</h2>
28+
<ul className="space-y-1">
29+
{edges.map((e, idx) => (
30+
<li
31+
key={idx}
32+
className={`flex justify-between rounded-md px-2 py-1 transition ${
33+
highlight?.edge &&
34+
highlight.edge.u === e.u &&
35+
highlight.edge.v === e.v
36+
? "bg-emerald-700 text-white"
37+
: "bg-gray-700 text-gray-300"
38+
}`}
39+
>
40+
<span>
41+
{e.u}{e.v}
42+
</span>
43+
<span>w = {e.w}</span>
44+
</li>
45+
))}
46+
</ul>
47+
</div>
48+
49+
{/* Step Info */}
50+
{highlight && (
51+
<div className="mt-4 text-center">
52+
{highlight.type === "relax" && (
53+
<p className="text-emerald-300">
54+
Iteration {highlight.iteration}: Relaxing edge {highlight.edge.u}{highlight.edge.v}
55+
</p>
56+
)}
57+
{highlight.type === "skip" && (
58+
<p className="text-gray-400">
59+
Iteration {highlight.iteration}: No update on edge {highlight.edge.u}{highlight.edge.v}
60+
</p>
61+
)}
62+
{highlight.type === "done" && (
63+
<p className="text-emerald-400 font-semibold">✅ Algorithm finished. Final distances updated!</p>
64+
)}
65+
{highlight.type === "negative-cycle" && (
66+
<p className="text-red-400 font-semibold">
67+
❌ Negative weight cycle detected on edge {highlight.edge.u}{highlight.edge.v}
68+
</p>
69+
)}
70+
</div>
71+
)}
72+
</div>
73+
);
74+
}
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
import React, { useState, useEffect } from "react";
2+
3+
export default function GraphVisualizer({ nodes = [], edges = [], distances = {}, highlight = {} }) {
4+
const [positions, setPositions] = useState({});
5+
6+
// Arrange nodes in a circle when nodes change
7+
useEffect(() => {
8+
if (nodes.length === 0) return;
9+
10+
const radius = 180;
11+
const centerX = 300;
12+
const centerY = 250;
13+
const newPositions = {};
14+
nodes.forEach((node, index) => {
15+
const angle = (2 * Math.PI * index) / nodes.length;
16+
newPositions[node] = {
17+
x: centerX + radius * Math.cos(angle),
18+
y: centerY + radius * Math.sin(angle),
19+
};
20+
});
21+
setPositions(newPositions);
22+
}, [nodes]);
23+
24+
return (
25+
<div className="w-full flex flex-col items-center mt-10 gap-6">
26+
<svg
27+
width="600"
28+
height="500"
29+
className="border border-gray-700 bg-gray-900 rounded-lg shadow-lg"
30+
>
31+
<defs>
32+
{/* Arrowhead marker */}
33+
<marker
34+
id="arrowhead"
35+
markerWidth="10"
36+
markerHeight="7"
37+
refX="8"
38+
refY="3.5"
39+
orient="auto"
40+
fill="currentColor"
41+
>
42+
<polygon points="0 0, 10 3.5, 0 7" />
43+
</marker>
44+
45+
{/* Glow effect for highlight */}
46+
<filter id="glow">
47+
<feGaussianBlur stdDeviation="3.5" result="coloredBlur" />
48+
<feMerge>
49+
<feMergeNode in="coloredBlur" />
50+
<feMergeNode in="SourceGraphic" />
51+
</feMerge>
52+
</filter>
53+
</defs>
54+
55+
{/* Edges */}
56+
{edges.map((e, idx) => {
57+
const from = positions[e.u];
58+
const to = positions[e.v];
59+
if (!from || !to) return null;
60+
61+
const isHighlighted =
62+
highlight?.edge && highlight.edge.u === e.u && highlight.edge.v === e.v;
63+
64+
return (
65+
<g key={idx}>
66+
<line
67+
x1={from.x}
68+
y1={from.y}
69+
x2={to.x}
70+
y2={to.y}
71+
stroke={isHighlighted ? "#34d399" : "#888"}
72+
strokeWidth={isHighlighted ? 5 : 2}
73+
markerEnd="url(#arrowhead)"
74+
style={{
75+
color: isHighlighted ? "#34d399" : "#fff",
76+
filter: isHighlighted ? "url(#glow)" : "none",
77+
transition: "stroke 0.3s ease, stroke-width 0.3s ease",
78+
strokeDasharray: isHighlighted ? "12 6" : "0",
79+
animation: isHighlighted ? "dashMove 1s linear infinite" : "none",
80+
}}
81+
/>
82+
<text
83+
x={(from.x + to.x) / 2}
84+
y={(from.y + to.y) / 2 - 8}
85+
fill="white"
86+
fontSize="12"
87+
textAnchor="middle"
88+
>
89+
{e.w}
90+
</text>
91+
</g>
92+
);
93+
})}
94+
95+
{/* Nodes */}
96+
{nodes.map((n) => {
97+
const pos = positions[n];
98+
if (!pos) return null;
99+
100+
const isHighlighted = highlight?.dist && highlight.dist[n] !== undefined;
101+
102+
return (
103+
<g key={n} style={{ transition: "all 0.3s ease" }}>
104+
<circle
105+
cx={pos.x}
106+
cy={pos.y}
107+
r="22"
108+
fill={isHighlighted ? "#065f46" : "#1f2937"}
109+
stroke={isHighlighted ? "#34d399" : "#9ca3af"}
110+
strokeWidth={isHighlighted ? 3 : 2}
111+
style={{ transition: "all 0.3s ease" }}
112+
/>
113+
<text
114+
x={pos.x}
115+
y={pos.y - 5}
116+
textAnchor="middle"
117+
fill="white"
118+
fontSize="14"
119+
fontWeight="bold"
120+
>
121+
{n}
122+
</text>
123+
<text
124+
x={pos.x}
125+
y={pos.y + 15}
126+
textAnchor="middle"
127+
fill="#d1d5db"
128+
fontSize="12"
129+
>
130+
{distances[n] === undefined || distances[n] === Infinity ? "∞" : distances[n]}
131+
</text>
132+
</g>
133+
);
134+
})}
135+
</svg>
136+
137+
{/* ✅ Answer Box */}
138+
<div className="w-[600px] bg-gray-800 border border-gray-700 rounded-lg shadow-md p-4">
139+
<h2 className="text-lg font-semibold text-white mb-2">📊 Result / Distances</h2>
140+
{Object.keys(distances).length === 0 ? (
141+
<p className="text-gray-400 text-sm">Run the algorithm to see results here.</p>
142+
) : (
143+
<div className="grid grid-cols-2 gap-2">
144+
{nodes.map((n) => (
145+
<div
146+
key={n}
147+
className="flex justify-between bg-gray-700 rounded px-3 py-1 text-sm text-gray-200"
148+
>
149+
<span className="font-semibold">Node {n}</span>
150+
<span>
151+
{distances[n] === undefined || distances[n] === Infinity
152+
? "∞"
153+
: distances[n]}
154+
</span>
155+
</div>
156+
))}
157+
</div>
158+
)}
159+
</div>
160+
161+
{/* Edge animation keyframes */}
162+
<style>{`
163+
@keyframes dashMove {
164+
to {
165+
stroke-dashoffset: -18;
166+
}
167+
}
168+
`}</style>
169+
</div>
170+
);
171+
}

src/pages/Homepage.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ const sections = [
3838
"Explore BFS, DFS, Kruskal’s, Prim’s, and more — all brought to life interactively.",
3939
phase: "Phase 2",
4040
img: "",
41-
link: "/graphs",
42-
flag: true
41+
link: "/graph",
42+
flag: false
4343
},
4444
{
4545
title: "Recursion & Backtracking",

0 commit comments

Comments
 (0)