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

// Standard bubble sort with generator events
for (let i = 0; i < n - 1; i++) {
// After each pass, the largest element of the unsorted portion bubbles to position n-1-i
for (let j = 0; j < n - 1 - i; j++) {
// compare pair (j, j+1)
yield { type: "compare", indices: [j, j + 1] };

if (arr[j] > arr[j + 1]) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
yield { type: "swap", indices: [j, j + 1], array: [...arr] };
}
}

// mark the settled position at the end of this pass
yield { type: "min", index: n - 1 - i };
}

yield { type: "done", array: arr };
}
38 changes: 38 additions & 0 deletions src/components/sorting/BubbleSortVisualizer.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 BubbleSortVisualizer({ 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/BubbleSort.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 BubbleSortVisualizer from "../../components/sorting/BubbleSortVisualizer";
import { bubbleSort } from "../../algorithms/sorting/bubbleSort";

export default function BubbleSort() {
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 = bubbleSort(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">
Bubble 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">
<BubbleSortVisualizer array={array} highlight={highlight} />
</div>

</div>
);
}
5 changes: 4 additions & 1 deletion 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 BubbleSort from "./BubbleSort";
import InsertionSort from "./InsertionSort";

export default function SortingPage() {
Expand All @@ -13,7 +14,8 @@ export default function SortingPage() {
case "insertion":
return <InsertionSort />;
// You can add more later like:
// case "bubble": return <BubbleSort />;
case "bubble":
return <BubbleSort />;
// case "merge": return <MergeSort />;
default:
return (
Expand All @@ -40,6 +42,7 @@ export default function SortingPage() {
>
<option value="">Select Algorithm</option>
<option value="selection">Selection Sort</option>
<option value="bubble">Bubble Sort</option>
<option value="insertion">Insertion Sort</option>
</select>

Expand Down
Loading