Quicksort
21 | 35 | 36 |├── public ├── favicon.png └── swapsound.mp3 ├── src ├── components │ └── Bar.js ├── utils │ ├── Shuffle.js │ ├── IsSorted.js │ ├── DisplayBars.js │ └── SoundManager.js ├── _reset.scss ├── algorithms │ ├── InsertionSort.js │ ├── BubbleSort.js │ ├── SelectionSort.js │ ├── BucketSort.js │ ├── HeapSort.js │ ├── RadixSort.js │ ├── Quicksort.js │ └── MergeSort.js ├── style.scss └── main.js ├── package.json ├── .gitignore ├── README.md └── index.html /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TylerMommsen/sorting-visualizer/HEAD/public/favicon.png -------------------------------------------------------------------------------- /public/swapsound.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TylerMommsen/sorting-visualizer/HEAD/public/swapsound.mp3 -------------------------------------------------------------------------------- /src/components/Bar.js: -------------------------------------------------------------------------------- 1 | export default function Bar() { 2 | const bar = document.createElement("div"); 3 | bar.classList.add("bar"); 4 | return bar; 5 | } 6 | -------------------------------------------------------------------------------- /src/utils/Shuffle.js: -------------------------------------------------------------------------------- 1 | export default function Shuffle(bars) { 2 | for (let i = bars.length - 1; i > 0; i--) { 3 | const j = Math.floor(Math.random() * (i + 1)); 4 | 5 | [bars[i], bars[j]] = [bars[j], bars[i]]; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/_reset.scss: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | margin: 0; 4 | padding: 0; 5 | color: $primary; 6 | font-family: sans-serif; 7 | } 8 | 9 | button { 10 | border: none; 11 | background-color: transparent; 12 | } 13 | -------------------------------------------------------------------------------- /src/utils/IsSorted.js: -------------------------------------------------------------------------------- 1 | export default async function IsSorted(arr) { 2 | for (let i = 0; i < arr.length - 1; i++) { 3 | if (arr[i] > arr[i + 1]) { 4 | return false; // Early return if any element is greater than its successor 5 | } 6 | } 7 | return true; // The array is sorted 8 | } 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sorting-visualizer", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "sass": "^1.74.1", 13 | "vite": "^5.2.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /src/algorithms/InsertionSort.js: -------------------------------------------------------------------------------- 1 | import { speed } from "../main"; 2 | import DisplayBars from "../utils/DisplayBars"; 3 | 4 | const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); 5 | 6 | export default async function InsertionSort(arr, left, right) { 7 | let i, key, j; 8 | 9 | for (i = left + 1; i <= right; i++) { 10 | key = arr[i]; 11 | j = i - 1; 12 | 13 | while (j >= 0 && arr[j] > key) { 14 | arr[j + 1] = arr[j]; 15 | DisplayBars(arr, [j + 1]); 16 | await delay(speed); 17 | 18 | j = j - 1; 19 | } 20 | 21 | arr[j + 1] = key; 22 | } 23 | 24 | DisplayBars(arr); 25 | 26 | return; 27 | } 28 | -------------------------------------------------------------------------------- /src/algorithms/BubbleSort.js: -------------------------------------------------------------------------------- 1 | import { speed } from "../main"; 2 | import DisplayBars from "../utils/DisplayBars"; 3 | 4 | const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); 5 | 6 | export default async function BubbleSort(bars) { 7 | let swapped = false; 8 | do { 9 | swapped = false; 10 | for (let i = 0; i < bars.length; i++) { 11 | if (bars[i - 1] > bars[i]) { 12 | swapped = true; 13 | [bars[i - 1], bars[i]] = [bars[i], bars[i - 1]]; 14 | DisplayBars(bars, [i - 1, i]); 15 | await delay(speed); 16 | } 17 | } 18 | } while (swapped); 19 | 20 | DisplayBars(bars); 21 | 22 | return; 23 | } 24 | -------------------------------------------------------------------------------- /src/algorithms/SelectionSort.js: -------------------------------------------------------------------------------- 1 | import { speed } from "../main"; 2 | import DisplayBars from "../utils/DisplayBars"; 3 | 4 | const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); 5 | 6 | export default async function SelectionSort(arr, n) { 7 | async function Swap(arr, xp, yp) { 8 | let temp = arr[xp]; 9 | arr[xp] = arr[yp]; 10 | arr[yp] = temp; 11 | DisplayBars(arr, [xp, yp]); 12 | await delay(speed); 13 | } 14 | 15 | async function Algorithm(arr, n) { 16 | let i, j, minIdx; 17 | 18 | for (i = 0; i < n - 1; i++) { 19 | minIdx = i; 20 | for (j = i + 1; j < n; j++) { 21 | if (arr[j] < arr[minIdx]) { 22 | minIdx = j; 23 | } 24 | } 25 | 26 | await Swap(arr, minIdx, i); 27 | } 28 | } 29 | 30 | await Algorithm(arr, n); 31 | 32 | DisplayBars(arr); 33 | 34 | return; 35 | } 36 | -------------------------------------------------------------------------------- /src/algorithms/BucketSort.js: -------------------------------------------------------------------------------- 1 | import DisplayBars from "../utils/DisplayBars"; 2 | import InsertionSort from "./InsertionSort"; 3 | 4 | export default async function BucketSort(arr) { 5 | let n = arr.length; 6 | let buckets = Array.from({ length: n }, () => []); 7 | 8 | // put array elements in different buckets 9 | for (let i = 0; i < n; i++) { 10 | let bi = Math.floor(n * arr[i]); 11 | bi = Math.min(bi, n - 1); // Ensure bi is within bounds 12 | buckets[bi].push(arr[i]); 13 | } 14 | 15 | // sort individual buckets using insertion sort 16 | for (let i = 0; i < n; i++) { 17 | await InsertionSort(buckets[i], 0, buckets[i].length - 1); 18 | } 19 | 20 | // concatenate all buckets into arr[] 21 | let index = 0; 22 | for (let i = 0; i < n; i++) { 23 | for (let j = 0; j < buckets[i].length; j++) { 24 | arr[index++] = buckets[i][j]; 25 | } 26 | } 27 | 28 | DisplayBars(arr); 29 | 30 | return; 31 | } 32 | -------------------------------------------------------------------------------- /src/algorithms/HeapSort.js: -------------------------------------------------------------------------------- 1 | import { speed } from "../main"; 2 | import DisplayBars from "../utils/DisplayBars"; 3 | 4 | const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); 5 | 6 | async function Heapify(arr, n, i) { 7 | let largest = i; 8 | let l = 2 * i + 1; 9 | let r = 2 * i + 2; 10 | 11 | if (l < n && arr[l] > arr[largest]) largest = l; 12 | 13 | if (r < n && arr[r] > arr[largest]) largest = r; 14 | 15 | if (largest != i) { 16 | let swap = arr[i]; 17 | arr[i] = arr[largest]; 18 | arr[largest] = swap; 19 | DisplayBars(arr, [i, largest]); 20 | await delay(speed); 21 | 22 | await Heapify(arr, n, largest); 23 | } 24 | } 25 | 26 | export default async function HeapSort(arr) { 27 | let n = arr.length; 28 | 29 | for (let i = Math.floor(n / 2) - 1; i >= 0; i--) { 30 | await Heapify(arr, n, i); 31 | } 32 | 33 | for (let i = n - 1; i > 0; i--) { 34 | let temp = arr[0]; 35 | arr[0] = arr[i]; 36 | arr[i] = temp; 37 | DisplayBars(arr, [0, i]); 38 | await delay(speed); 39 | 40 | await Heapify(arr, i, 0); 41 | } 42 | 43 | DisplayBars(arr); 44 | 45 | return; 46 | } 47 | -------------------------------------------------------------------------------- /src/utils/DisplayBars.js: -------------------------------------------------------------------------------- 1 | import { soundOn } from "../main"; 2 | import { playSound } from "./SoundManager"; 3 | import Bar from "../components/Bar"; 4 | 5 | export default async function DisplayBars(bars, currentlySwapped) { 6 | // give each bar a relative value between 0 and 100 to give a nice staircase look when displaying finished sort 7 | let normalizedBars = bars.map((bar) => { 8 | return (bar * 100) / bars.length; 9 | }); 10 | 11 | let barContainer = document.getElementById("bar-container"); 12 | barContainer.innerHTML = ""; 13 | for (let i = 0; i < normalizedBars.length; i++) { 14 | let currBar = Bar(); 15 | currBar.style.height = normalizedBars[i] + "%"; 16 | currBar.style.width = 100 / normalizedBars.length + "%"; 17 | 18 | if (currentlySwapped && currentlySwapped.includes(i)) { 19 | currBar.style.background = "linear-gradient(to top right, #ff6a00, #ff9143)"; 20 | const pitchRate = bars[i] / Math.max(...bars) + 0.5; 21 | // Play the sound with adjusted pitch 22 | if (soundOn) { 23 | playSound(pitchRate); 24 | } 25 | } 26 | 27 | barContainer.appendChild(currBar); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/algorithms/RadixSort.js: -------------------------------------------------------------------------------- 1 | import { speed } from "../main"; 2 | import DisplayBars from "../utils/DisplayBars"; 3 | 4 | const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); 5 | 6 | function GetPosition(num, place) { 7 | return Math.floor(Math.abs(num) / Math.pow(10, place)) % 10; 8 | } 9 | 10 | function GetMax(arr) { 11 | let max = 0; 12 | for (let num of arr) { 13 | if (max < Math.floor(num).toString().length) { 14 | max = Math.floor(num).toString().length; 15 | } 16 | } 17 | return max; 18 | } 19 | 20 | export default async function RadixSort(arr) { 21 | const max = GetMax(arr); // Get the maximum number of digits 22 | 23 | for (let i = 0; i < max; i++) { 24 | let buckets = Array.from({ length: 10 }, () => []); 25 | 26 | // Distribute the array elements into buckets 27 | for (let j = 0; j < arr.length; j++) { 28 | buckets[GetPosition(arr[j], i)].push(arr[j]); 29 | 30 | DisplayBars(arr, [j]); 31 | await delay(speed); 32 | } 33 | 34 | // Flatten the buckets back into the array 35 | arr = [].concat(...buckets); 36 | } 37 | 38 | DisplayBars(arr); 39 | 40 | return arr; 41 | } 42 | -------------------------------------------------------------------------------- /src/algorithms/Quicksort.js: -------------------------------------------------------------------------------- 1 | import { speed } from "../main"; 2 | import DisplayBars from "../utils/DisplayBars"; 3 | 4 | const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); 5 | 6 | export default async function Quicksort(arr, low, high) { 7 | // function that handles partitioning the array and returning the partition index 8 | async function Partition(arr, low, high) { 9 | let pivot = arr[high]; 10 | let i = low - 1; 11 | 12 | for (let j = low; j <= high - 1; j++) { 13 | if (arr[j] < pivot) { 14 | i++; 15 | [arr[i], arr[j]] = [arr[j], arr[i]]; // swap elements 16 | DisplayBars(arr, [i, j]); 17 | await delay(speed); 18 | } 19 | } 20 | 21 | [arr[i + 1], arr[high]] = [arr[high], arr[i + 1]]; // swap pivot to its correct position 22 | return i + 1; 23 | } 24 | 25 | // the main quicksort algorithm 26 | async function Algorithm(arr, low, high) { 27 | if (low < high) { 28 | // pi is the partitioning index, arr[pi] is now at the right place 29 | let pi = await Partition(arr, low, high); 30 | 31 | // Seperately sort elements before parition and after partition 32 | await Algorithm(arr, low, pi - 1); 33 | await Algorithm(arr, pi + 1, high); 34 | } 35 | } 36 | 37 | await Algorithm(arr, low, high); 38 | 39 | DisplayBars(arr); 40 | 41 | return; 42 | } 43 | -------------------------------------------------------------------------------- /src/utils/SoundManager.js: -------------------------------------------------------------------------------- 1 | let sound = null; 2 | 3 | // Function to load sound 4 | async function loadSound(url) { 5 | const response = await fetch(url); 6 | const arrayBuffer = await response.arrayBuffer(); 7 | const audioBuffer = await audioContext.decodeAudioData(arrayBuffer); 8 | return audioBuffer; 9 | } 10 | 11 | // Initialize the Audio Context 12 | const audioContext = new (window.AudioContext || window.webkitAudioContext)(); 13 | const gainNode = audioContext.createGain(); 14 | gainNode.connect(audioContext.destination); 15 | 16 | let playingSounds = 0; 17 | 18 | export async function playSound(pitchRate) { 19 | const sound = await getSound(); 20 | 21 | playingSounds++; 22 | updateGain(playingSounds); 23 | 24 | const source = audioContext.createBufferSource(); 25 | source.buffer = sound; 26 | source.playbackRate.value = pitchRate; 27 | source.connect(gainNode); 28 | source.onended = () => { 29 | playingSounds--; 30 | updateGain(playingSounds); 31 | }; 32 | source.start(); 33 | } 34 | 35 | function updateGain(numberOfSounds) { 36 | const baseVolume = 0.5; 37 | // Check if numberOfSounds is 0 to avoid division by 0 38 | if (numberOfSounds <= 1) { 39 | gainNode.gain.value = baseVolume; // Set to default/full volume when no sounds are playing 40 | } else { 41 | // Decrease volume as more sounds play 42 | gainNode.gain.value = baseVolume / Math.sqrt(numberOfSounds); 43 | } 44 | } 45 | 46 | export async function getSound() { 47 | if (!sound) { 48 | sound = await loadSound("/swapsound.mp3"); 49 | } 50 | return sound; 51 | } 52 | -------------------------------------------------------------------------------- /src/algorithms/MergeSort.js: -------------------------------------------------------------------------------- 1 | import { speed } from "../main"; 2 | import DisplayBars from "../utils/DisplayBars"; 3 | 4 | const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); 5 | 6 | export async function Merge(arr, left, middle, right) { 7 | const n1 = middle - left + 1; 8 | const n2 = right - middle; 9 | 10 | // create temp arrays 11 | const L = new Array(n1); 12 | const R = new Array(n2); 13 | 14 | for (let i = 0; i < n1; i++) { 15 | L[i] = arr[left + i]; 16 | } 17 | 18 | for (let j = 0; j < n2; j++) { 19 | R[j] = arr[middle + 1 + j]; 20 | } 21 | 22 | // merge the temp arrays back into arr[left..right] 23 | let i = 0; // initial index of the first subarray 24 | let j = 0; // initial index of the second subarray 25 | let k = left; // initial index of the merged subarray 26 | 27 | while (i < n1 && j < n2) { 28 | if (L[i] <= R[j]) { 29 | arr[k] = L[i]; 30 | DisplayBars(arr, [k]); 31 | i++; 32 | } else { 33 | arr[k] = R[j]; 34 | DisplayBars(arr, [k]); 35 | j++; 36 | } 37 | await delay(speed); 38 | k++; 39 | } 40 | 41 | // copy the remaining elements of L[] if there are any 42 | while (i < n1) { 43 | arr[k] = L[i]; 44 | DisplayBars(arr, [k]); 45 | await delay(speed); 46 | i++; 47 | k++; 48 | } 49 | 50 | // copy the remaining elements of R[] if there are any 51 | while (j < n2) { 52 | arr[k] = R[j]; 53 | DisplayBars(arr, [k]); 54 | await delay(speed); 55 | j++; 56 | k++; 57 | } 58 | } 59 | 60 | export default async function MergeSort(arr, left, right) { 61 | async function Algorithm(arr, left, right) { 62 | if (left >= right) { 63 | return; // returns recursively 64 | } 65 | 66 | const middle = left + Math.floor((right - left) / 2); 67 | await Algorithm(arr, left, middle); 68 | await Algorithm(arr, middle + 1, right); 69 | await Merge(arr, left, middle, right); 70 | } 71 | 72 | await Algorithm(arr, left, right - 1); 73 | 74 | DisplayBars(arr); 75 | 76 | return; 77 | } 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sorting Visualizer 2 | 3 | Sorting algorithm visualization tool to illustrate and compare the mechanics and efficiency of various sorting algorithms in real-time. This tool allows you to change the algorithm, adjust the number of items in the dataset between 1 and 1,000 and change the speed at which the algorithm executes between 1ms and 1000ms. 4 | 5 | ## Live Demo 6 | 7 | [View Live Site Here](https://tylermommsen-sorting-visualizer.vercel.app/) 8 | 9 | ## Built With 10 | 11 |
Quicksort
21 | 35 | 36 |