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
34 changes: 34 additions & 0 deletions src/algorithms/sorting/InsertionSort.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// src/algorithms/sorting/InsertionSort.js
export function* insertionSort(array) {
const arr = [...array];
const n = arr.length;

for (let i = 1; i < n; i++) {
// mark the current index being considered (matches selectionSort behavior)
yield { type: "compare", indices: [i] };

// perform insertion by swapping adjacent elements until correct position
let j = i;
while (j > 0) {
// compare the pair we're about to potentially swap
yield { type: "compare", indices: [j - 1, j] };

if (arr[j - 1] > arr[j]) {
// swap adjacent
[arr[j - 1], arr[j]] = [arr[j], arr[j - 1]];
yield { type: "swap", indices: [j - 1, j], array: [...arr] };
j--;
} else {
break;
}
}

// if element moved, indicate insertion position (use "min" to match visual cue)
if (j !== i) {
yield { type: "min", index: j };
}
}

yield { type: "done", array: arr };
}

38 changes: 38 additions & 0 deletions src/components/sorting/InsertionSortVisualizer.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 InsertionSortVisualizer({ 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>
);
}
84 changes: 84 additions & 0 deletions src/pages/sorting/InsertionSort.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import React, { useState } from "react";
import { Toaster } from "react-hot-toast";
import InsertionSortVisualizer from "../../components/sorting/InsertionSortVisualizer";
import { insertionSort } from "../../algorithms/sorting/InsertionSort";

export default function InsertionSort() {
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 = insertionSort(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">
Insertion 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">
<InsertionSortVisualizer 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
@@ -1,6 +1,7 @@
// src/pages/SortingPage.jsx
import React, { useState } from "react";
import SelectionSort from "./SelectionSort";
import InsertionSort from "./InsertionSort";

export default function SortingPage() {
const [selectedAlgo, setSelectedAlgo] = useState("");
Expand All @@ -9,6 +10,8 @@ export default function SortingPage() {
switch (selectedAlgo) {
case "selection":
return <SelectionSort />;
case "insertion":
return <InsertionSort />;
// You can add more later like:
// case "bubble": return <BubbleSort />;
// case "merge": return <MergeSort />;
Expand Down Expand Up @@ -37,6 +40,7 @@ export default function SortingPage() {
>
<option value="">Select Algorithm</option>
<option value="selection">Selection Sort</option>
<option value="insertion">Insertion Sort</option>
</select>

<button
Expand Down
Loading