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

// Helper generator function for the recursive quicksort
function* quickSortHelper(low, high) {
if (low < high) {
// Partition the array and get the pivot index
yield* partition(low, high);
const pivotIndex = arr.lastPivotIndex;

// Recursively sort left subarray
yield* quickSortHelper(low, pivotIndex - 1);

// Recursively sort right subarray
yield* quickSortHelper(pivotIndex + 1, high);
} else if (low === high) {
// Single element is already sorted
yield { type: "sorted", index: low };
}
}

// Partition function using Lomuto partition scheme
function* partition(low, high) {
// Choose the last element as pivot
const pivot = arr[high];
yield { type: "pivot", index: high };

let i = low - 1; // Index of smaller element

for (let j = low; j < high; j++) {
// Highlight current partition range
yield { type: "partition", indices: [low, high] };

// Compare current element with pivot
yield { type: "compare", indices: [j, high] };

if (arr[j] < pivot) {
i++;
if (i !== j) {
// Swap elements
[arr[i], arr[j]] = [arr[j], arr[i]];
yield { type: "swap", indices: [i, j], array: [...arr] };
}
}
}

// Place pivot in its correct position
if (i + 1 !== high) {
[arr[i + 1], arr[high]] = [arr[high], arr[i + 1]];
yield { type: "swap", indices: [i + 1, high], array: [...arr] };
}

// Mark pivot as sorted in its final position
yield { type: "sorted", index: i + 1 };

// Store pivot index for recursive calls
arr.lastPivotIndex = i + 1;
}

// Start the quicksort process
yield* quickSortHelper(0, arr.length - 1);

// Mark all elements as sorted
yield { type: "done", array: arr };
}
38 changes: 38 additions & 0 deletions src/components/sorting/QuickSortVisualizer.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>
);
}
2 changes: 1 addition & 1 deletion src/pages/Homepage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ const Homepage = () => {
Explore Now <ArrowRight size={18} />
</button>
<a
href="https://github.com/"
href="https://github.com/OPCODE-Open-Spring-Fest/Algo-Visualizer"
target="_blank"
rel="noopener noreferrer"
className="px-6 py-3 border border-gray-400 hover:bg-white/10 text-gray-200 font-semibold rounded-full flex items-center gap-2 transition-all duration-300"
Expand Down
77 changes: 77 additions & 0 deletions src/pages/sorting/QuickSort.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import React, { useState } from "react";
import { Toaster } from "react-hot-toast";
import QuickSortVisualizer from "../../components/sorting/QuickSortVisualizer";
import { quickSort } from "../../algorithms/sorting/quicksort";

export default function QuickSort() {
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 = quickSort(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">
Quick 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">
<QuickSortVisualizer 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 QuickSort from "./QuickSort";
import MergeSort from "./MergeSort";

export default function SortingPage() {
Expand All @@ -17,6 +18,8 @@ export default function SortingPage() {
// You can add more later like:
case "bubble":
return <BubbleSort />;
case "quick":
return <QuickSort />;
// case "merge": return <MergeSort />;
case "merge":
return <MergeSort />;
Expand Down Expand Up @@ -47,6 +50,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="quick">Quick Sort</option>
<option value="merge">Merge Sort</option>
</select>

Expand Down
Loading