Skip to content

Commit d03965b

Browse files
committed
Merge branch 'main' into feat/algo
2 parents d93d71c + ca18504 commit d03965b

18 files changed

+1131
-94
lines changed

jsconfig.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
{
22
"compilerOptions": {
33
"baseUrl": ".",
4-
"ignoreDeprecations": "6.0",
54
"paths": {
65
"@/*": ["src/*"]
76
}

package-lock.json

Lines changed: 27 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"lucide-react": "^0.545.0",
2424
"react": "^19.1.1",
2525
"react-dom": "^19.1.1",
26+
"react-hot-toast": "^2.6.0",
2627
"react-redux": "^9.2.0",
2728
"react-router-dom": "^7.9.4",
2829
"socket.io-client": "^4.8.1",

src/App.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import React from "react";
22
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
3-
import Homepage from "../src/pages/Homepage.jsx";
43
import UnionFindPage from "../src/pages/graph/UnionFind.jsx"; // ✅ Import Union-Find Page
5-
4+
import SortingPage from "./pages/sorting/SortingPage";
5+
import Homepage from "./pages/Homepage.jsx";
66
function App() {
77
return (
88
<Router>
99
<Routes>
1010
<Route path="/" element={<Homepage />} />
1111
<Route path="/graph/union-find" element={<UnionFindPage />} /> {/* ✅ Added route */}
12+
<Route path="/sorting" element={<SortingPage />} />
1213
</Routes>
1314
</Router>
1415
);

src/algorithms/graph/unionFind.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// src/algorithms/graph/unionFind.js
22

3-
export class UnionFind {
3+
class UnionFind {
44
constructor(n) {
55
this.parent = Array.from({ length: n }, (_, i) => i);
66
this.rank = Array(n).fill(0);
@@ -68,3 +68,5 @@ export class UnionFind {
6868
return this.steps;
6969
}
7070
}
71+
72+
export default UnionFind;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// src/algorithms/sorting/InsertionSort.js
2+
export function* insertionSort(array) {
3+
const arr = [...array];
4+
const n = arr.length;
5+
6+
for (let i = 1; i < n; i++) {
7+
// mark the current index being considered (matches selectionSort behavior)
8+
yield { type: "compare", indices: [i] };
9+
10+
// perform insertion by swapping adjacent elements until correct position
11+
let j = i;
12+
while (j > 0) {
13+
// compare the pair we're about to potentially swap
14+
yield { type: "compare", indices: [j - 1, j] };
15+
16+
if (arr[j - 1] > arr[j]) {
17+
// swap adjacent
18+
[arr[j - 1], arr[j]] = [arr[j], arr[j - 1]];
19+
yield { type: "swap", indices: [j - 1, j], array: [...arr] };
20+
j--;
21+
} else {
22+
break;
23+
}
24+
}
25+
26+
// if element moved, indicate insertion position (use "min" to match visual cue)
27+
if (j !== i) {
28+
yield { type: "min", index: j };
29+
}
30+
}
31+
32+
yield { type: "done", array: arr };
33+
}
34+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// src/algorithms/sorting/bubbleSort.js
2+
export function* bubbleSort(array) {
3+
const arr = [...array];
4+
const n = arr.length;
5+
6+
// Standard bubble sort with generator events
7+
for (let i = 0; i < n - 1; i++) {
8+
// After each pass, the largest element of the unsorted portion bubbles to position n-1-i
9+
for (let j = 0; j < n - 1 - i; j++) {
10+
// compare pair (j, j+1)
11+
yield { type: "compare", indices: [j, j + 1] };
12+
13+
if (arr[j] > arr[j + 1]) {
14+
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
15+
yield { type: "swap", indices: [j, j + 1], array: [...arr] };
16+
}
17+
}
18+
19+
// mark the settled position at the end of this pass
20+
yield { type: "min", index: n - 1 - i };
21+
}
22+
23+
yield { type: "done", array: arr };
24+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// src/algorithms/sorting/selectionSort.js
2+
export function* selectionSort(array) {
3+
const arr = [...array];
4+
const n = arr.length;
5+
6+
for (let i = 0; i < n - 1; i++) {
7+
let minIndex = i;
8+
yield { type: "compare", indices: [i] };
9+
10+
for (let j = i + 1; j < n; j++) {
11+
yield { type: "compare", indices: [minIndex, j] };
12+
13+
if (arr[j] < arr[minIndex]) {
14+
minIndex = j;
15+
yield { type: "min", index: minIndex };
16+
}
17+
}
18+
19+
if (minIndex !== i) {
20+
[arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
21+
yield { type: "swap", indices: [i, minIndex], array: [...arr] };
22+
}
23+
}
24+
25+
yield { type: "done", array: arr };
26+
}

src/components/graph/UnionFindVisualizer.jsx

Lines changed: 112 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,120 @@
22
import { motion } from "framer-motion";
33

44
export default function UnionFindVisualizer({ nodes, parent, highlights }) {
5+
// Calculate positions for nodes in a grid layout
6+
const getNodePosition = (index) => {
7+
const cols = Math.ceil(Math.sqrt(nodes.length));
8+
const row = Math.floor(index / cols);
9+
const col = index % cols;
10+
return { row, col };
11+
};
12+
13+
// Generate connections for visualization
14+
const connections = [];
15+
nodes.forEach((node, index) => {
16+
if (parent[index] !== index) {
17+
const parentPos = getNodePosition(parent[index]);
18+
const childPos = getNodePosition(index);
19+
connections.push({
20+
from: childPos,
21+
to: parentPos,
22+
child: index,
23+
parent: parent[index]
24+
});
25+
}
26+
});
27+
528
return (
6-
<div className="flex flex-wrap justify-center gap-6 mt-6">
7-
{nodes.map((node, index) => {
8-
const isRoot = parent[index] === index;
9-
const color =
10-
highlights.current === index
11-
? "bg-blue-500"
12-
: highlights.root === index
13-
? "bg-green-500"
14-
: highlights.union.includes(index)
15-
? "bg-red-500"
16-
: "bg-gray-300";
29+
<div className="relative">
30+
{/* SVG for connections */}
31+
<svg
32+
className="absolute inset-0 w-full h-full pointer-events-none"
33+
style={{ minHeight: '300px' }}
34+
>
35+
{connections.map((conn, index) => {
36+
const fromX = (conn.from.col + 0.5) * (100 / Math.ceil(Math.sqrt(nodes.length))) + '%';
37+
const fromY = (conn.from.row + 0.5) * (100 / Math.ceil(Math.sqrt(nodes.length))) + '%';
38+
const toX = (conn.to.col + 0.5) * (100 / Math.ceil(Math.sqrt(nodes.length))) + '%';
39+
const toY = (conn.to.row + 0.5) * (100 / Math.ceil(Math.sqrt(nodes.length))) + '%';
40+
41+
const isHighlighted = highlights.union.includes(conn.child) ||
42+
highlights.current === conn.child ||
43+
highlights.root === conn.parent;
44+
45+
return (
46+
<motion.line
47+
key={index}
48+
x1={fromX}
49+
y1={fromY}
50+
x2={toX}
51+
y2={toY}
52+
stroke={isHighlighted ? "#ef4444" : "#6b7280"}
53+
strokeWidth={isHighlighted ? "3" : "2"}
54+
strokeDasharray={isHighlighted ? "5,5" : "none"}
55+
initial={{ pathLength: 0 }}
56+
animate={{ pathLength: 1 }}
57+
transition={{ duration: 0.5, delay: index * 0.1 }}
58+
/>
59+
);
60+
})}
61+
</svg>
62+
63+
{/* Nodes */}
64+
<div className="grid gap-4 justify-center" style={{
65+
gridTemplateColumns: `repeat(${Math.ceil(Math.sqrt(nodes.length))}, 1fr)`,
66+
maxWidth: '600px',
67+
margin: '0 auto'
68+
}}>
69+
{nodes.map((node, index) => {
70+
const isRoot = parent[index] === index;
71+
const color =
72+
highlights.current === index
73+
? "bg-blue-500"
74+
: highlights.root === index
75+
? "bg-green-500"
76+
: highlights.union.includes(index)
77+
? "bg-red-500"
78+
: highlights.path.includes(index)
79+
? "bg-purple-500"
80+
: "bg-gray-600";
81+
82+
return (
83+
<motion.div
84+
key={index}
85+
className={`w-20 h-20 flex flex-col items-center justify-center text-white font-semibold rounded-full shadow-lg border-2 border-white/20 ${color} relative`}
86+
layout
87+
animate={{
88+
scale: highlights.current === index ? 1.2 : 1,
89+
boxShadow: highlights.current === index ? "0 0 20px rgba(59, 130, 246, 0.5)" : "0 4px 6px rgba(0, 0, 0, 0.1)"
90+
}}
91+
transition={{ duration: 0.3 }}
92+
>
93+
<div className="text-lg font-bold">{node}</div>
94+
<div className="text-xs opacity-80">p:{parent[index]}</div>
95+
{isRoot && (
96+
<div className="absolute -top-2 -right-2 w-6 h-6 bg-yellow-400 rounded-full flex items-center justify-center">
97+
<span className="text-[10px] font-bold text-black">R</span>
98+
</div>
99+
)}
100+
{highlights.current === index && (
101+
<div className="absolute -top-3 -left-3 w-6 h-6 bg-blue-400 rounded-full flex items-center justify-center animate-pulse">
102+
<span className="text-[10px] font-bold text-white">F</span>
103+
</div>
104+
)}
105+
</motion.div>
106+
);
107+
})}
108+
</div>
17109

18-
return (
19-
<motion.div
20-
key={index}
21-
className={`w-16 h-16 flex flex-col items-center justify-center text-white font-semibold rounded-full shadow-md ${color}`}
22-
layout
23-
animate={{ scale: highlights.current === index ? 1.2 : 1 }}
24-
>
25-
<div>{node}</div>
26-
<div className="text-xs">p:{parent[index]}</div>
27-
{isRoot && <div className="text-[10px]">(root)</div>}
28-
</motion.div>
29-
);
30-
})}
110+
{/* Additional Info */}
111+
<div className="mt-6 text-center text-sm text-gray-300">
112+
<p>Each node shows: <span className="text-white font-semibold">Node Number</span> | <span className="text-gray-400">Parent</span></p>
113+
<p className="mt-1">
114+
<span className="text-yellow-400">R</span> = Root |
115+
<span className="text-blue-400"> F</span> = Finding |
116+
<span className="text-red-400"> Red</span> = Unioning
117+
</p>
118+
</div>
31119
</div>
32120
);
33121
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import React from "react";
2+
3+
const COLORS = {
4+
default: "bg-blue-500 shadow-[0_0_10px_#3b82f6]",
5+
comparing: "bg-yellow-400 shadow-[0_0_12px_#facc15]",
6+
min: "bg-green-500 shadow-[0_0_12px_#22c55e]",
7+
swap: "bg-red-500 shadow-[0_0_12px_#ef4444]",
8+
};
9+
10+
export default function BubbleSortVisualizer({ array, highlight }) {
11+
const maxValue = Math.max(...array, 1); // Avoid division by zero
12+
const containerHeight = 288; // px (matches h-72)
13+
14+
return (
15+
<div className="flex items-end justify-center space-x-2 h-72 mt-10 transition-all duration-500">
16+
{array.map((value, idx) => {
17+
let color = COLORS.default;
18+
if (highlight?.type === "compare" && highlight.indices?.includes(idx))
19+
color = COLORS.comparing;
20+
if (highlight?.type === "min" && highlight.index === idx)
21+
color = COLORS.min;
22+
if (highlight?.type === "swap" && highlight.indices?.includes(idx))
23+
color = COLORS.swap;
24+
25+
// Normalize height relative to the maximum value
26+
const height = Math.max((value / maxValue) * containerHeight, 15); // minimum 15px for visibility
27+
28+
return (
29+
<div
30+
key={idx}
31+
className={`${color} w-6 transition-all duration-300 rounded-t-md`}
32+
style={{ height: `${height}px` }}
33+
></div>
34+
);
35+
})}
36+
</div>
37+
);
38+
}

0 commit comments

Comments
 (0)