Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions src/algorithms/sorting/mergeSort.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// src/algorithms/sorting/mergeSort.js
export function* mergeSort(array) {
const arr = [...array];
const n = arr.length;

if (n === 0) {
yield { type: "done", array: arr };
return;
}

function* merge(l, m, r) {
const left = arr.slice(l, m + 1);
const right = arr.slice(m + 1, r + 1);
let i = 0;
let j = 0;
let k = l;

while (i < left.length && j < right.length) {
// highlight the two elements being compared (use their original indices)
yield { type: "compare", indices: [l + i, m + 1 + j] };

if (left[i] <= right[j]) {
arr[k] = left[i];
yield { type: "swap", indices: [k, l + i], array: [...arr] };
i++;
} else {
arr[k] = right[j];
yield { type: "swap", indices: [k, m + 1 + j], array: [...arr] };
j++;
}
k++;
}

while (i < left.length) {
arr[k] = left[i];
yield { type: "swap", indices: [k, l + i], array: [...arr] };
i++;
k++;
}

while (j < right.length) {
arr[k] = right[j];
yield { type: "swap", indices: [k, m + 1 + j], array: [...arr] };
j++;
k++;
}

// mark merged positions as "min" to indicate they're in final place for this merge
for (let idx = l; idx <= r; idx++) {
yield { type: "min", index: idx };
}
}

function* mergeSortRec(l, r) {
if (l >= r) return;
const m = Math.floor((l + r) / 2);
yield* mergeSortRec(l, m);
yield* mergeSortRec(m + 1, r);
yield* merge(l, m, r);
}

yield* mergeSortRec(0, n - 1);
yield { type: "done", array: arr };
}
38 changes: 38 additions & 0 deletions src/components/sorting/MergeSortVisualizer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from "react";

const COLORS = {
default: "bg-blue-500 shadow-[0_0_10px_#3b82f6]",
comparing: "bg-yellow-400 shadow-[0_0_12px_#facc15]",
min: "bg-green-500 shadow-[0_0_12px_#22c55e]",
swap: "bg-red-500 shadow-[0_0_12px_#ef4444]",
};

export default function MergeSortVisualizer({ array, highlight }) {
const maxValue = Math.max(...array, 1); // Avoid division by zero
const containerHeight = 288; // px (matches h-72)

return (
<div className="flex items-end justify-center space-x-2 h-72 mt-10 transition-all duration-500">
{array.map((value, idx) => {
let color = COLORS.default;
if (highlight?.type === "compare" && highlight.indices?.includes(idx))
color = COLORS.comparing;
if (highlight?.type === "min" && highlight.index === idx)
color = COLORS.min;
if (highlight?.type === "swap" && highlight.indices?.includes(idx))
color = COLORS.swap;

// Normalize height relative to the maximum value
const height = Math.max((value / maxValue) * containerHeight, 15); // minimum 15px for visibility

return (
<div
key={idx}
className={`${color} w-6 transition-all duration-300 rounded-t-md`}
style={{ height: `${height}px` }}
></div>
);
})}
</div>
);
}
83 changes: 83 additions & 0 deletions src/pages/sorting/MergeSort.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React, { useState } from "react";
import { Toaster } from "react-hot-toast";
import MergeSortVisualizer from "../../components/sorting/MergeSortVisualizer";
import { mergeSort } from "../../algorithms/sorting/mergeSort";

export default function MergeSort() {
const [array, setArray] = useState([]);
const [input, setInput] = useState("");
const [highlight, setHighlight] = useState(null);
const [isRunning, setIsRunning] = useState(false);

const handleStart = async () => {
if (isRunning || array.length === 0) return;
setIsRunning(true);

const gen = mergeSort(array);
for (let step of gen) {
setHighlight(step);
if (step.array) setArray([...step.array]);
await new Promise((r) => setTimeout(r, 500));
}

setHighlight({ type: "done" });
setIsRunning(false);
};

const handleReset = () => {
setArray([]);
setInput("");
setHighlight(null);
};

const handleInput = (e) => {
setInput(e.target.value);
const numbers = e.target.value
.split(",")
.map((n) => parseInt(n.trim()))
.filter((n) => !isNaN(n));
setArray(numbers);
};

return (
<div className="min-h-screen bg-black text-gray-200 flex flex-col items-center p-6">
<Toaster position="top-center" />
<h1 className="text-4xl font-extrabold mb-8 text-indigo-400 drop-shadow-lg">
Merge Sort Visualizer
</h1>

<input
type="text"
value={input}
onChange={handleInput}
placeholder="Enter numbers separated by commas"
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"
/>

<div className="space-x-4 mt-6">
<button
onClick={handleStart}
disabled={isRunning}
className={`${
isRunning
? "bg-indigo-700 text-gray-300 cursor-not-allowed"
: "bg-indigo-600 hover:bg-indigo-500"
} px-6 py-2 rounded-lg text-white font-semibold shadow-md transition-all duration-300`}
>
{isRunning ? "Sorting..." : "Start Visualization"}
</button>
<button
onClick={handleReset}
className="bg-gray-700 hover:bg-gray-600 px-6 py-2 rounded-lg text-white font-semibold shadow-md transition-all duration-300"
>
Reset
</button>
</div>

<div className="mt-15">
<MergeSortVisualizer array={array} highlight={highlight} />
</div>

</div>
);
}
4 changes: 4 additions & 0 deletions src/pages/sorting/SortingPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, { useState } from "react";
import SelectionSort from "./SelectionSort";
import BubbleSort from "./BubbleSort";
import InsertionSort from "./InsertionSort";
import MergeSort from "./MergeSort";

export default function SortingPage() {
const [selectedAlgo, setSelectedAlgo] = useState("");
Expand All @@ -17,6 +18,8 @@ export default function SortingPage() {
case "bubble":
return <BubbleSort />;
// case "merge": return <MergeSort />;
case "merge":
return <MergeSort />;
default:
return (
<div className="text-gray-400 text-lg mt-20 text-center">
Expand Down Expand Up @@ -44,6 +47,7 @@ export default function SortingPage() {
<option value="selection">Selection Sort</option>
<option value="bubble">Bubble Sort</option>
<option value="insertion">Insertion Sort</option>
<option value="merge">Merge Sort</option>
</select>

<button
Expand Down
Loading