Skip to content

Commit d3d3ffa

Browse files
Merge pull request #18 from Vaibhav0527/feature/Merge-Sort-Visualizer
implemented Merge-Sort-Visualizer
2 parents 1174033 + 8c6f65c commit d3d3ffa

File tree

4 files changed

+189
-0
lines changed

4 files changed

+189
-0
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// src/algorithms/sorting/mergeSort.js
2+
export function* mergeSort(array) {
3+
const arr = [...array];
4+
const n = arr.length;
5+
6+
if (n === 0) {
7+
yield { type: "done", array: arr };
8+
return;
9+
}
10+
11+
function* merge(l, m, r) {
12+
const left = arr.slice(l, m + 1);
13+
const right = arr.slice(m + 1, r + 1);
14+
let i = 0;
15+
let j = 0;
16+
let k = l;
17+
18+
while (i < left.length && j < right.length) {
19+
// highlight the two elements being compared (use their original indices)
20+
yield { type: "compare", indices: [l + i, m + 1 + j] };
21+
22+
if (left[i] <= right[j]) {
23+
arr[k] = left[i];
24+
yield { type: "swap", indices: [k, l + i], array: [...arr] };
25+
i++;
26+
} else {
27+
arr[k] = right[j];
28+
yield { type: "swap", indices: [k, m + 1 + j], array: [...arr] };
29+
j++;
30+
}
31+
k++;
32+
}
33+
34+
while (i < left.length) {
35+
arr[k] = left[i];
36+
yield { type: "swap", indices: [k, l + i], array: [...arr] };
37+
i++;
38+
k++;
39+
}
40+
41+
while (j < right.length) {
42+
arr[k] = right[j];
43+
yield { type: "swap", indices: [k, m + 1 + j], array: [...arr] };
44+
j++;
45+
k++;
46+
}
47+
48+
// mark merged positions as "min" to indicate they're in final place for this merge
49+
for (let idx = l; idx <= r; idx++) {
50+
yield { type: "min", index: idx };
51+
}
52+
}
53+
54+
function* mergeSortRec(l, r) {
55+
if (l >= r) return;
56+
const m = Math.floor((l + r) / 2);
57+
yield* mergeSortRec(l, m);
58+
yield* mergeSortRec(m + 1, r);
59+
yield* merge(l, m, r);
60+
}
61+
62+
yield* mergeSortRec(0, n - 1);
63+
yield { type: "done", array: arr };
64+
}
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 MergeSortVisualizer({ 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+
}

src/pages/sorting/MergeSort.jsx

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import React, { useState } from "react";
2+
import { Toaster } from "react-hot-toast";
3+
import MergeSortVisualizer from "../../components/sorting/MergeSortVisualizer";
4+
import { mergeSort } from "../../algorithms/sorting/mergeSort";
5+
6+
export default function MergeSort() {
7+
const [array, setArray] = useState([]);
8+
const [input, setInput] = useState("");
9+
const [highlight, setHighlight] = useState(null);
10+
const [isRunning, setIsRunning] = useState(false);
11+
12+
const handleStart = async () => {
13+
if (isRunning || array.length === 0) return;
14+
setIsRunning(true);
15+
16+
const gen = mergeSort(array);
17+
for (let step of gen) {
18+
setHighlight(step);
19+
if (step.array) setArray([...step.array]);
20+
await new Promise((r) => setTimeout(r, 500));
21+
}
22+
23+
setHighlight({ type: "done" });
24+
setIsRunning(false);
25+
};
26+
27+
const handleReset = () => {
28+
setArray([]);
29+
setInput("");
30+
setHighlight(null);
31+
};
32+
33+
const handleInput = (e) => {
34+
setInput(e.target.value);
35+
const numbers = e.target.value
36+
.split(",")
37+
.map((n) => parseInt(n.trim()))
38+
.filter((n) => !isNaN(n));
39+
setArray(numbers);
40+
};
41+
42+
return (
43+
<div className="min-h-screen bg-black text-gray-200 flex flex-col items-center p-6">
44+
<Toaster position="top-center" />
45+
<h1 className="text-4xl font-extrabold mb-8 text-indigo-400 drop-shadow-lg">
46+
Merge Sort Visualizer
47+
</h1>
48+
49+
<input
50+
type="text"
51+
value={input}
52+
onChange={handleInput}
53+
placeholder="Enter numbers separated by commas"
54+
className="border-2 border-indigo-500 bg-gray-900 text-indigo-200 rounded-lg p-3 w-96 text-center shadow-lg focus:ring-2 focus:ring-indigo-400 outline-none"
55+
/>
56+
57+
<div className="space-x-4 mt-6">
58+
<button
59+
onClick={handleStart}
60+
disabled={isRunning}
61+
className={`${
62+
isRunning
63+
? "bg-indigo-700 text-gray-300 cursor-not-allowed"
64+
: "bg-indigo-600 hover:bg-indigo-500"
65+
} px-6 py-2 rounded-lg text-white font-semibold shadow-md transition-all duration-300`}
66+
>
67+
{isRunning ? "Sorting..." : "Start Visualization"}
68+
</button>
69+
<button
70+
onClick={handleReset}
71+
className="bg-gray-700 hover:bg-gray-600 px-6 py-2 rounded-lg text-white font-semibold shadow-md transition-all duration-300"
72+
>
73+
Reset
74+
</button>
75+
</div>
76+
77+
<div className="mt-15">
78+
<MergeSortVisualizer array={array} highlight={highlight} />
79+
</div>
80+
81+
</div>
82+
);
83+
}

src/pages/sorting/SortingPage.jsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import React, { useState } from "react";
33
import SelectionSort from "./SelectionSort";
44
import BubbleSort from "./BubbleSort";
55
import InsertionSort from "./InsertionSort";
6+
import MergeSort from "./MergeSort";
67

78
export default function SortingPage() {
89
const [selectedAlgo, setSelectedAlgo] = useState("");
@@ -17,6 +18,8 @@ export default function SortingPage() {
1718
case "bubble":
1819
return <BubbleSort />;
1920
// case "merge": return <MergeSort />;
21+
case "merge":
22+
return <MergeSort />;
2023
default:
2124
return (
2225
<div className="text-gray-400 text-lg mt-20 text-center">
@@ -44,6 +47,7 @@ export default function SortingPage() {
4447
<option value="selection">Selection Sort</option>
4548
<option value="bubble">Bubble Sort</option>
4649
<option value="insertion">Insertion Sort</option>
50+
<option value="merge">Merge Sort</option>
4751
</select>
4852

4953
<button

0 commit comments

Comments
 (0)