diff --git a/package-lock.json b/package-lock.json
index c952e7e..616c49a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7,6 +7,7 @@
"": {
"name": "algo-visualizer",
"version": "0.0.0",
+ "license": "ISC",
"dependencies": {
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-label": "^2.1.7",
diff --git a/package.json b/package.json
index 611f96e..2f009e5 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,20 @@
{
"name": "algo-visualizer",
- "private": true,
"version": "0.0.0",
+ "private": true,
+ "description": "**AlgoVisualizer** is an open-source platform that allows learners and developers to **visualize algorithms** in real-time — from sorting and searching to graph traversal, recursion, and dynamic programming. \r Built with **React + Tailwind CSS**, it aims to make algorithmic concepts more intuitive and interactive.",
+ "homepage": "https://github.com/PresenceOP-Coder/Algo-Visualizer#readme",
+ "bugs": {
+ "url": "https://github.com/PresenceOP-Coder/Algo-Visualizer/issues"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/PresenceOP-Coder/Algo-Visualizer.git"
+ },
+ "license": "ISC",
+ "author": "",
"type": "module",
+ "main": "commitlint.config.js",
"scripts": {
"dev": "vite",
"build": "vite build",
diff --git a/src/algorithms/dynamic-programming/kmpSearchAlgo.js b/src/algorithms/dynamic-programming/kmpAlgo.js
similarity index 100%
rename from src/algorithms/dynamic-programming/kmpSearchAlgo.js
rename to src/algorithms/dynamic-programming/kmpAlgo.js
diff --git a/src/algorithms/sorting/countingSort.js b/src/algorithms/sorting/countingSort.js
new file mode 100644
index 0000000..10219ad
--- /dev/null
+++ b/src/algorithms/sorting/countingSort.js
@@ -0,0 +1,80 @@
+export function getCountingSortSteps(array) {
+ const steps = [];
+ const n = array.length;
+
+ if (n === 0) {
+ return { steps };
+ }
+
+ const max = Math.max(...array);
+ const count = new Array(max + 1).fill(0);
+ const output = new Array(n).fill(0);
+ const initialInput = [...array];
+
+ const pushStep = (phase, message, highlight = {}) => {
+ steps.push({
+ phase,
+ input: [...initialInput],
+ count: [...count],
+ output: [...output],
+ message,
+ highlight
+ });
+ };
+
+ pushStep(1, "Starting Phase 1: Count Frequencies. Creating 'Count' array of size (max + 1) = " + (max + 1) + ".", {});
+
+ // Phase 1: Count frequencies
+ for (let i = 0; i < n; i++) {
+ const val = array[i];
+ count[val]++;
+ pushStep(1, `Read input[${i}] = ${val}. Incrementing count[${val}] to ${count[val]}.`, {
+ input: i,
+ count: val
+ });
+ }
+
+ pushStep(2, "Starting Phase 2: Calculate Cumulative Sums.", {});
+
+ for (let i = 1; i <= max; i++) {
+ const prevCount = count[i - 1];
+ count[i] += prevCount;
+ pushStep(2, `Calculating count[${i}] = count[${i}] + count[${i - 1}] = ${count[i] - prevCount} + ${prevCount} = ${count[i]}.`, {
+ count: i,
+ countRead: i - 1
+ });
+ }
+
+ pushStep(3, "Starting Phase 3: Build Output Array (Iterating input in reverse).", {});
+
+ for (let i = n - 1; i >= 0; i--) {
+ const val = array[i];
+ pushStep(3, `Reading input[${i}] = ${val}.`, {
+ input: i
+ });
+
+ const posIndex = count[val] - 1;
+ pushStep(3, `Position for ${val} is count[${val}] - 1 = ${count[val]} - 1 = ${posIndex}.`, {
+ input: i,
+ countRead: val
+ });
+
+ output[posIndex] = val;
+ pushStep(3, `Placing ${val} at output[${posIndex}].`, {
+ input: i,
+ countRead: val,
+ output: posIndex
+ });
+
+ count[val]--;
+ pushStep(3, `Decrementing count[${val}] to ${count[val]}.`, {
+ input: i,
+ count: val,
+ output: posIndex
+ });
+ }
+
+ pushStep(4, "Algorithm Complete. The 'Output' array is now sorted.", {});
+
+ return { steps };
+}
\ No newline at end of file
diff --git a/src/components/dynamic-programming/kmpSearchAlgovisual.jsx b/src/components/dynamic-programming/kmpSearchAlgovisual.jsx
index 1820d39..306614d 100644
--- a/src/components/dynamic-programming/kmpSearchAlgovisual.jsx
+++ b/src/components/dynamic-programming/kmpSearchAlgovisual.jsx
@@ -1,5 +1,5 @@
import React, { useState, useMemo, useEffect, useRef } from "react";
-//import { computeLPSSteps, kmpSearchSteps } from "@/algorithms/string/kmpAlgo";
+import { computeLPSSteps, kmpSearchSteps } from "@/algorithms/dynamic-programming/kmpAlgo";
const LPSDisplay = ({ pattern, lpsState }) => {
const { lps, highlight, message } = lpsState;
diff --git a/src/components/sorting/CountingSortVisualizer.jsx b/src/components/sorting/CountingSortVisualizer.jsx
new file mode 100644
index 0000000..2d050f7
--- /dev/null
+++ b/src/components/sorting/CountingSortVisualizer.jsx
@@ -0,0 +1,265 @@
+import React, { useState, useMemo, useEffect, useRef } from "react";
+import { getCountingSortSteps } from "../../algorithms/sorting/countingSort.js";
+
+const ArrayDisplay = ({ title, array, highlightIndex = -1, readIndex = -1, indicesLabel = "Index" }) => {
+ return (
+
+
{title}
+
+ {array.map((value, index) => {
+ const isCurrent = index === highlightIndex;
+ const isReading = index === readIndex;
+
+ let cellClass = "bg-gray-700 border-gray-600 text-gray-200 hover:border-gray-500";
+ if (isCurrent) {
+ cellClass = "bg-blue-600 border-blue-400 shadow-lg text-white font-bold transform scale-105"; // Write/Current
+ } else if (isReading) {
+ cellClass = "bg-yellow-600 border-yellow-400 text-white"; // Read
+ }
+
+ return (
+
+
{indicesLabel} {index}
+
{value === null ? '?' : value}
+
+ );
+ })}
+
+
+ );
+};
+
+
+const SPEED_OPTIONS = {
+ "Slow": 1500,
+ "Medium": 500,
+ "Fast": 200,
+};
+
+export default function CountingSort() {
+ const [input, setInput] = useState("4, 1, 3, 4, 0, 2, 1, 7");
+
+ const [steps, setSteps] = useState([]);
+ const [currentStep, setCurrentStep] = useState(0);
+ const [isPlaying, setIsPlaying] = useState(false);
+ const [speed, setSpeed] = useState(SPEED_OPTIONS["Medium"]);
+ const timerRef = useRef(null);
+
+ const handleCompute = () => {
+ const parsedArray = input
+ .split(',')
+ .map(s => Number(s.trim()))
+ .filter(n => !isNaN(n) && n >= 0);
+
+ if (parsedArray.length === 0 || parsedArray.length > 20) {
+ alert("Please enter 1 to 20 non-negative numbers, separated by commas.");
+ return;
+ }
+
+ const maxVal = Math.max(...parsedArray);
+ if (maxVal > 50) {
+ alert("The maximum value in the array must be 50 or less for optimal visualization.");
+ return;
+ }
+
+ setIsPlaying(false);
+ const { steps: newSteps } = getCountingSortSteps(parsedArray);
+ setSteps(newSteps);
+ setCurrentStep(0);
+ };
+
+ useEffect(() => {
+ handleCompute();
+ }, []);
+
+ useEffect(() => {
+ if (isPlaying && currentStep < steps.length - 1) {
+ timerRef.current = setInterval(() => {
+ setCurrentStep((prevStep) => prevStep + 1);
+ }, speed);
+ } else if (currentStep === steps.length - 1) {
+ setIsPlaying(false);
+ }
+
+ return () => {
+ clearInterval(timerRef.current);
+ };
+ }, [isPlaying, currentStep, steps.length, speed]);
+
+ const togglePlay = () => {
+ if (currentStep === steps.length - 1) {
+ setCurrentStep(0);
+ setIsPlaying(true);
+ } else {
+ setIsPlaying(!isPlaying);
+ }
+ };
+
+ const handleNext = () => {
+ setIsPlaying(false);
+ if (currentStep < steps.length - 1) setCurrentStep(currentStep + 1);
+ };
+
+ const handlePrev = () => {
+ setIsPlaying(false);
+ if (currentStep > 0) setCurrentStep(currentStep - 1);
+ };
+
+ const currentState = useMemo(() => steps[currentStep] || {}, [steps, currentStep]);
+ const { phase, input: inputArray, count, output, highlight, message } = currentState;
+
+ const isFinalStep = phase === 4;
+
+ return (
+
+
+
+ Counting Sort
+
+
+
+
+ ❓ What is Counting Sort?
+
+
+
+ Counting Sort is a non-comparison algorithm that sorts integers. It works in three phases:
+
+
+
+ Phase 1 (Frequency): Count the occurrences of each unique element and store it in a `Count` array.
+
+
+ Phase 2 (Cumulative Sum): Modify the `Count` array so each element stores the sum of all previous counts. This gives the "ending position" of each element.
+
+
+ Phase 3 (Output): Iterate the `Input` array in reverse (for stability), place each element in its correct position in the `Output` array, and decrement its count.
+
+
+
+
+
+
+
+ Input Array (non-negative, max val 50):
+ setInput(e.target.value)}
+ placeholder="e.g., 4, 1, 3, 4, 0"
+ />
+
+
+ Re-Visualize
+
+
+
+ {steps.length > 0 ? (
+ <>
+
+
+ {isFinalStep && !isPlaying ? "Replay ▶️" : isPlaying ? "Pause ⏸️" : "Play ▶️"}
+
+
+ 0 ? "bg-purple-600 hover:bg-purple-700 text-white" : "bg-gray-600 text-gray-400 cursor-not-allowed"}`}
+ onClick={handlePrev}
+ disabled={currentStep === 0}
+ >
+ < Prev
+
+
+ Next >
+
+
+
+ Speed:
+ setSpeed(Number(e.target.value))}
+ >
+ {Object.entries(SPEED_OPTIONS).map(([label, ms]) => (
+ {label}
+ ))}
+
+
+
+
+
+
+ Step {currentStep + 1} / {steps.length}
+ {phase && (Phase {phase} / 3) }
+
+
+
+
+
+
Current Action
+
+ {message || 'Starting computation...'}
+
+
+
+
+
+
+
+
+
+ {isFinalStep && (
+
+
+ 🎉 Algorithm Complete!
+
+
+ )}
+
+
+ >
+ ) : (
+
+
Welcome to the Counting Sort Visualizer!
+
Enter a list of non-negative numbers (max value 50).
+
Click Re-Visualize to begin.
+
+ )}
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/pages/sorting/CountingSort.jsx b/src/pages/sorting/CountingSort.jsx
new file mode 100644
index 0000000..299550f
--- /dev/null
+++ b/src/pages/sorting/CountingSort.jsx
@@ -0,0 +1,6 @@
+import CountingSortVisualizer from "@/components/sorting/CountingSortVisualizer";
+import React from "react";
+
+export default function CountingSortPage() {
+ return ;
+}
\ No newline at end of file
diff --git a/src/pages/sorting/SortingPage.jsx b/src/pages/sorting/SortingPage.jsx
index caaf784..56985df 100644
--- a/src/pages/sorting/SortingPage.jsx
+++ b/src/pages/sorting/SortingPage.jsx
@@ -1,4 +1,3 @@
-// src/pages/SortingPage.jsx
import React, { useState } from "react";
import SelectionSort from "./SelectionSort";
import BubbleSort from "./BubbleSort";
@@ -6,6 +5,7 @@ import InsertionSort from "./InsertionSort";
import QuickSort from "./QuickSort";
import MergeSort from "./MergeSort";
import RadixSort from "./RadixSort";
+import CountingSort from "./CountingSort";
export default function SortingPage() {
const [selectedAlgo, setSelectedAlgo] = useState("");
@@ -16,16 +16,16 @@ export default function SortingPage() {
return ;
case "insertion":
return ;
- // You can add more later like:
case "bubble":
return ;
case "quick":
return ;
- // case "merge": return ;
case "merge":
return ;
case "radix":
return
+ case "counting":
+ return
default:
return (
@@ -37,7 +37,6 @@ export default function SortingPage() {
return (
- {/* Left Sidebar */}
Sorting Panel
@@ -52,10 +51,11 @@ export default function SortingPage() {
Select Algorithm
Selection Sort
Bubble Sort
- Insertion Sort
+ Insertion Sort
Quick Sort
Merge Sort
Radix Sort
+ Counting Sort
- {/* Right Visualization Area */}
+
{renderAlgorithm()}
);
-}
+}
\ No newline at end of file