├── .gitignore ├── README.md ├── docs └── _config.yml ├── package-lock.json ├── package.json ├── public ├── favicon.png ├── index.html ├── manifest.json └── robots.txt └── src ├── App.css ├── App.js ├── App.test.js ├── index.css ├── index.js ├── logo.svg ├── reportWebVitals.js ├── setupTests.js └── sortingvisualizer ├── SortingVisualizer.css ├── SortingVisualizer.jsx ├── algorithms.js └── sortingAlgorithms.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React App to visualize Sorting Algorithms 2 | 3 | This app is a visual way to see merge sort, bubble sort, insertion sort and quick sort sorting algorithms in action. Available at: https://tuomaskivioja.github.io/SortingAlgorithmsVisualized/ 4 | 5 | Idea inspired by Clement Mihailescu. 6 | 7 | ## Implementation 8 | 9 | By clicking 'genrate array' the user generates a number array of length 250 which is stored in the SortingVisualizer object's state. This is then translated into array bars by loopin gover the array and giving each bar a height of 0.1% times the value of the array. 10 | 11 | By clicking on a button for any given algorithm, the user can see the array bars being sorted where the height of the bar represents a value in an array of numbers. 12 | 13 | ## The Algorithms 14 | 15 | I have implemented the algorithms using numbers only in algorithms.js. Then, in sortingAlgorithms.js I have translated these into animations that I pass into the relevant algorithms inside SortingVisualizer.jsx which actually translates these animations into height values for the array bars in the visualizer. 16 | 17 | ## The Future 18 | 19 | In the future, I will be implementing a toggle where the user can switch between a single algorithm view to a comparison window where multiple algorithms can be run in parallel. 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-slate -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "homepage": "https://tuomaskivioja.github.io/SortingAlgorithmsVisualized", 3 | "name": "sorting-visualizer", 4 | "version": "0.1.0", 5 | "private": true, 6 | "dependencies": { 7 | "@testing-library/jest-dom": "^5.16.2", 8 | "@testing-library/react": "^12.1.3", 9 | "@testing-library/user-event": "^13.5.0", 10 | "gh-pages": "^3.2.3", 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2", 13 | "react-scripts": "5.0.0", 14 | "web-vitals": "^2.1.4" 15 | }, 16 | "scripts": { 17 | "predeploy": "npm run build", 18 | "deploy": "gh-pages -d build", 19 | "start": "react-scripts start", 20 | "build": "react-scripts build", 21 | "test": "react-scripts test", 22 | "eject": "react-scripts eject" 23 | }, 24 | "eslintConfig": { 25 | "extends": [ 26 | "react-app", 27 | "react-app/jest" 28 | ] 29 | }, 30 | "browserslist": { 31 | "production": [ 32 | ">0.2%", 33 | "not dead", 34 | "not op_mini all" 35 | ], 36 | "development": [ 37 | "last 1 chrome version", 38 | "last 1 firefox version", 39 | "last 1 safari version" 40 | ] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuomaskivioja/SortingAlgorithmsVisualized/8918e58ff48be851dc833a29272208cf28cf8a70/public/favicon.png -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 16 | 25 | Sorting Visualizer 26 | 27 | 28 | 29 |
30 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import logo from './logo.svg'; 2 | import SortingVisualizer from './sortingvisualizer/SortingVisualizer'; 3 | import './App.css'; 4 | 5 | function App() { 6 | return ( 7 |
8 | 9 |
10 | ); 11 | } 12 | 13 | export default App; 14 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /src/sortingvisualizer/SortingVisualizer.css: -------------------------------------------------------------------------------- 1 | .app { 2 | width: 100vw; 3 | height: 100vh; 4 | } 5 | 6 | .navbar { 7 | height: 20%; 8 | width: 100%; 9 | display: flex; 10 | flex-direction: row; 11 | justify-content: space-around; 12 | align-items: center; 13 | background-color:midnightblue; 14 | } 15 | 16 | .array-container { 17 | width: 100%; 18 | height: 80%; 19 | display: flex; 20 | flex-direction: row; 21 | justify-content: center; 22 | align-items: center; 23 | } 24 | .array-bars-container { 25 | width: 85%; 26 | height: 80%; 27 | } 28 | .array-bar { 29 | width: 0.2%; 30 | display: inline-block; 31 | margin-left: 0.1%; 32 | margin-right: 0.1%; 33 | } 34 | .timeBox { 35 | font-size: 2em; 36 | } -------------------------------------------------------------------------------- /src/sortingvisualizer/SortingVisualizer.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './SortingVisualizer.css'; 3 | import {getMergeSortAnimations, getBubbleSortAnimations, getSelectionSortAnimations, getQuickSortAnimations} from './sortingAlgorithms.js'; 4 | 5 | // Change this value for the speed of the animations. 6 | const ANIMATION_SPEED_MS = 1; 7 | 8 | // Change this value for the number of bars (value) in the array. 9 | const NUMBER_OF_ARRAY_BARS = 250; 10 | 11 | // This is the main color of the array bars. 12 | const PRIMARY_COLOR = 'turquoise'; 13 | 14 | // This is the color of array bars that are being compared throughout the animations. 15 | const SECONDARY_COLOR = 'red'; 16 | 17 | function randomInteger(min, max) { 18 | return Math.floor(Math.random() * (max - min + 1)) + min; 19 | } 20 | 21 | export default class SortingVisualizer extends React.Component { 22 | constructor(props) { 23 | super(props); 24 | 25 | this.state = { 26 | array: [], 27 | }; 28 | } 29 | 30 | componentDidMount() { 31 | this.resetArray(); 32 | } 33 | 34 | runSort(animations) { 35 | for (let i = 0; i < animations.length; i++) { 36 | const arrayBars = document.getElementsByClassName('array-bar'); 37 | const isColorChange = i % 3 !== 2; 38 | if (isColorChange) { 39 | const [barOneIdx, barTwoIdx] = animations[i]; 40 | const barOneStyle = arrayBars[barOneIdx].style; 41 | const barTwoStyle = arrayBars[barTwoIdx].style; 42 | const color = i % 3 === 0 ? SECONDARY_COLOR : PRIMARY_COLOR; 43 | setTimeout(() => { 44 | barOneStyle.backgroundColor = color; 45 | barTwoStyle.backgroundColor = color; 46 | }, i * ANIMATION_SPEED_MS); 47 | } 48 | 49 | else if (animations[i] === "noSwap") { 50 | setTimeout(() => { 51 | }, i * ANIMATION_SPEED_MS); 52 | } 53 | 54 | else { 55 | setTimeout(() => { 56 | const [barOneIdx, newHeight1, barTwoIdx, newHeight2] = animations[i]; 57 | const barOneStyle = arrayBars[barOneIdx].style; 58 | barOneStyle.height = `${newHeight1 * 0.1}%`; 59 | const barTwoStyle = arrayBars[barTwoIdx].style; 60 | barTwoStyle.height = `${newHeight2 * 0.1}%`; 61 | }, i * ANIMATION_SPEED_MS); 62 | } 63 | } 64 | } 65 | 66 | resetArray() { 67 | const array = [] 68 | for (let i = 0; i < NUMBER_OF_ARRAY_BARS; i++) { 69 | array.push(randomInteger(5, 1000)) 70 | } 71 | this.setState({array}); 72 | } 73 | 74 | mergeSort() { 75 | const animations = getMergeSortAnimations(this.state.array); 76 | for (let i = 0; i < animations.length; i++) { 77 | const arrayBars = document.getElementsByClassName('array-bar'); 78 | const isColorChange = i % 3 !== 2; 79 | if (isColorChange) { 80 | const [barOneIdx, barTwoIdx] = animations[i]; 81 | const barOneStyle = arrayBars[barOneIdx].style; 82 | const barTwoStyle = arrayBars[barTwoIdx].style; 83 | const color = i % 3 === 0 ? SECONDARY_COLOR : PRIMARY_COLOR; 84 | setTimeout(() => { 85 | barOneStyle.backgroundColor = color; 86 | barTwoStyle.backgroundColor = color; 87 | }, i * ANIMATION_SPEED_MS); 88 | } else { 89 | setTimeout(() => { 90 | const [barOneIdx, newHeight] = animations[i]; 91 | const barOneStyle = arrayBars[barOneIdx].style; 92 | barOneStyle.height = `${newHeight * 0.1}%`; 93 | }, i * ANIMATION_SPEED_MS); 94 | } 95 | } 96 | } 97 | 98 | quickSort() { 99 | const animations = getQuickSortAnimations(this.state.array); 100 | this.runSort(animations) 101 | } 102 | 103 | selectionSort() { 104 | const animations = getSelectionSortAnimations(this.state.array); 105 | this.runSort(animations) 106 | } 107 | 108 | bubbleSort() { 109 | const animations = getBubbleSortAnimations(this.state.array); 110 | this.runSort(animations) 111 | } 112 | 113 | // addTime(start, end) { 114 | // const arrayContainer = document.querySelector(".array-container"); 115 | // const bars = arrayContainer.firstChild; 116 | // const timeTaken = Math.round((end - start) / 1000) 117 | // const timeBox = document.createElement('div') 118 | // timeBox.classList.add("timeBox") 119 | // timeBox.innerHTML = `Elapsed time: ${timeTaken} seconds!` 120 | // arrayContainer.insertBefore(timeBox, bars) 121 | // } 122 | 123 | render() { 124 | const {array} = this.state; 125 | 126 | return ( 127 |
128 |
129 | 130 | 131 | 132 | 133 | 134 |
135 |
136 |
137 | {array.map((value, i) => ( 138 |
145 | ))} 146 |
147 |
148 |
149 | ); 150 | }; 151 | } -------------------------------------------------------------------------------- /src/sortingvisualizer/algorithms.js: -------------------------------------------------------------------------------- 1 | 2 | // this contains my implementations of the algotithms with just numbers for practice 3 | 4 | const Mergesort = (array) => { 5 | 6 | function mergeArrays(arr1, arr2) { 7 | 8 | const sortedArr = [] 9 | 10 | while (arr1.length && arr2.length) { 11 | if (arr1[0] > arr2[0]) { 12 | sortedArr.push(arr2.shift()) 13 | } 14 | else { 15 | sortedArr.push(arr1.shift()) 16 | } 17 | } 18 | 19 | while (arr1.length) { 20 | sortedArr.push(arr1.shift()) 21 | } 22 | while (arr2.length) { 23 | sortedArr.push(arr2.shift()) 24 | } 25 | 26 | return sortedArr 27 | 28 | } 29 | 30 | if (array.length < 2) { 31 | return array 32 | } 33 | const middle = Math.floor(array.length/2) 34 | 35 | const subarrayA = array.slice(0, middle) 36 | const subarrayB = array.slice(middle, array.length) 37 | 38 | const sortedSubarrayA = Mergesort(subarrayA) 39 | const sortedSubarrayB = Mergesort(subarrayB) 40 | 41 | return mergeArrays(sortedSubarrayA, sortedSubarrayB) 42 | 43 | } 44 | 45 | const Quicksort = (array) => { 46 | // a function for swapping 2 elements 47 | function swap(arr, i1, i2) { 48 | const temp = arr[i2] 49 | arr[i2] = arr[i1] 50 | arr[i1] = temp 51 | } 52 | // base case 53 | if (array.length < 2) { 54 | return array 55 | } 56 | // select pivot element randomly 57 | const pivotIndex = 0 58 | // swap pivot to index 0. pivot element is thus forth at array[0] 59 | swap(array, pivotIndex, 0) 60 | // i denotes boundary between smaller and larger elements than pivot 61 | let i = 1 62 | // iterate over all elements larger than 0 (pivot). If the element is larger than 63 | // pivot, swap it with ith element and advance i 64 | for (let j = 1; j < array.length; j++) { 65 | //console.log(`iteration: ${j}`) 66 | if (array[j] < array[0]) { 67 | //console.log(array[j]) 68 | swap(array, i, j) 69 | i++ 70 | } 71 | } 72 | swap(array, 0, i-1) 73 | const leftArray = array.slice(0, i-1) 74 | const rightArray = array.slice(i, array.length) 75 | let completeArray = [] 76 | // recursively apply the same to left and right subarrays and merge 77 | completeArray = completeArray.concat(...Quicksort(leftArray)) 78 | completeArray.push(array[i-1]) 79 | completeArray = completeArray.concat(...Quicksort(rightArray)) 80 | return completeArray 81 | } 82 | 83 | const Bubblesort = (array) => { 84 | const length = array.length; 85 | for (let i = 0; i < length; i++) { 86 | for (let j = 0; j < length-i; j++) { 87 | if(array[j] > array[j+1]) { 88 | //Swap the numbers 89 | let temp = array[j] 90 | array[j] = array[j+1]; 91 | array[j+1] = temp; 92 | } 93 | } 94 | } 95 | 96 | return array; 97 | } 98 | 99 | const Selectionsort = (array) => { 100 | const length = array.length; 101 | for(let i = 0; i < length; i++){ 102 | // set current index as minimum 103 | let min = i; 104 | let temp = array[i]; 105 | for(let j = i+1; j < length; j++){ 106 | if (array[j] < array[min]){ 107 | //update minimum if current is lower that what we had previously 108 | min = j; 109 | } 110 | } 111 | array[i] = array[min]; 112 | array[min] = temp; 113 | } 114 | return array; 115 | } 116 | 117 | 118 | let functions = {Mergesort, Quicksort, Bubblesort, Selectionsort} 119 | 120 | export default functions -------------------------------------------------------------------------------- /src/sortingvisualizer/sortingAlgorithms.js: -------------------------------------------------------------------------------- 1 | export function getBubbleSortAnimations(array) { 2 | const animations = [] 3 | for (let i = 0; i < array.length; i++) { 4 | for (let j = 0; j < array.length-1-i; j++) { 5 | // change colours 6 | animations.push([j,j+1]) 7 | // revert colours 8 | animations.push([j,j+1]) 9 | if(array[j] > array[j+1]) { 10 | //Swap the numbers 11 | let temp = array[j] 12 | animations.push([j,array[j+1],j+1,array[j]]) 13 | array[j] = array[j+1]; 14 | array[j+1] = temp; 15 | } 16 | else { 17 | animations.push("noSwap") 18 | } 19 | } 20 | } 21 | return animations 22 | } 23 | 24 | export function getSelectionSortAnimations(array) { 25 | const animations = [] 26 | for(let i = 0; i < array.length; i++){ 27 | // set current index as minimum 28 | let min = i; 29 | let temp = array[i]; 30 | for(let j = i+1; j < array.length; j++){ 31 | // change colours 32 | animations.push([min,j]) 33 | // revert colours 34 | animations.push([min,j]) 35 | //no swap yet 36 | animations.push("noSwap") 37 | if (array[j] < array[min]){ 38 | //update minimum if current is lower that what we had previously 39 | min = j; 40 | } 41 | } 42 | // change colours 43 | animations.push([i,min]) 44 | // revert colours 45 | animations.push([i,min]) 46 | // swap the numbers 47 | animations.push([i,array[min],min,array[i]]) 48 | array[i] = array[min]; 49 | array[min] = temp; 50 | } 51 | return animations; 52 | } 53 | 54 | export function getQuickSortAnimations(array) { 55 | const animations = [] 56 | // a function for swapping 2 elements 57 | function swap(arr, i1, i2) { 58 | const temp = arr[i2] 59 | // change colours 60 | animations.push([i1,i2]) 61 | // revert colours 62 | animations.push([i1,i2]) 63 | // swap the numbers 64 | animations.push([i1,arr[i2],i2,arr[i1]]) 65 | arr[i2] = arr[i1] 66 | arr[i1] = temp 67 | } 68 | // base case 69 | if (array.length < 2) { 70 | return array 71 | } 72 | // select pivot element randomly 73 | const pivotIndex = 0 74 | // swap pivot to index 0. pivot element is thus forth at array[0] 75 | swap(array, pivotIndex, 0) 76 | // i denotes boundary between smaller and larger elements than pivot 77 | let i = 1 78 | // iterate over all elements larger than 0 (pivot). If the element is larger than 79 | // pivot, swap it with ith element and advance i 80 | for (let j = 1; j < array.length; j++) { 81 | //console.log(`iteration: ${j}`) 82 | if (array[j] < array[0]) { 83 | //console.log(array[j]) 84 | swap(array, i, j) 85 | i++ 86 | } 87 | else { 88 | // change colours 89 | animations.push([i,j]) 90 | // revert colours 91 | animations.push([i,j]) 92 | //no swap yet 93 | animations.push("noSwap") 94 | } 95 | } 96 | swap(array, 0, i-1) 97 | const leftArray = array.slice(0, i-1) 98 | const rightArray = array.slice(i, array.length) 99 | // recursively apply the same to left and right subarrays and merge 100 | const auxiliaryArray = array.slice() 101 | QuickSortHelper(leftArray, animations, auxiliaryArray, 0) 102 | QuickSortHelper(rightArray, animations, auxiliaryArray, i) 103 | return animations 104 | } 105 | 106 | function QuickSortHelper(mainArray, animations, auxArray, startIdx) { 107 | // base case 108 | if (mainArray.length < 2) { 109 | return 110 | } 111 | function swapNpush(arr, i1, i2, auxArr, Idx1, Idx2) { 112 | // change colours 113 | animations.push([Idx1,Idx2]) 114 | // revert colours 115 | animations.push([Idx1,Idx2]) 116 | // swap the numbers 117 | animations.push([Idx1,auxArr[Idx2],Idx2,auxArr[Idx1]]) 118 | swap(arr, i1, i2) 119 | swap(auxArr, Idx1, Idx2) 120 | } 121 | function swap(arr, i1, i2) { 122 | const temp = arr[i2] 123 | arr[i2] = arr[i1] 124 | arr[i1] = temp 125 | } 126 | const pivotIndex = 0 127 | swapNpush(mainArray, pivotIndex, 0, auxArray, startIdx, startIdx) 128 | let i = 1 129 | let iTracker = startIdx + 1 130 | let jTracker = startIdx + 1 131 | for (let j = 1; j < mainArray.length; j++) { 132 | if (mainArray[j] < mainArray[0]) { 133 | //console.log(array[j]) 134 | swapNpush(mainArray, i, j, auxArray, iTracker, jTracker) 135 | i++ 136 | iTracker++ 137 | jTracker++ 138 | } 139 | else { 140 | // change colours 141 | animations.push([iTracker,jTracker]) 142 | // revert colours 143 | animations.push([iTracker,jTracker]) 144 | //no swap yet 145 | animations.push("noSwap") 146 | jTracker++ 147 | } 148 | } 149 | swapNpush(mainArray, 0, i-1, auxArray, startIdx, iTracker-1) 150 | const leftArray = mainArray.slice(0, i-1) 151 | const rightArray = mainArray.slice(i, mainArray.length) 152 | // recursively apply the same to left and right subarrays and merge 153 | QuickSortHelper(leftArray, animations, auxArray, startIdx) 154 | QuickSortHelper(rightArray, animations, auxArray, iTracker) 155 | 156 | } 157 | export function getMergeSortAnimations(array) { 158 | const animations = []; 159 | if (array.length <= 1) return array; 160 | const auxiliaryArray = array.slice(); 161 | mergeSortHelper(array, 0, array.length - 1, auxiliaryArray, animations); 162 | return animations; 163 | } 164 | 165 | function mergeSortHelper( 166 | mainArray, 167 | startIdx, 168 | endIdx, 169 | auxiliaryArray, 170 | animations, 171 | ) { 172 | if (startIdx === endIdx) return; 173 | const middleIdx = Math.floor((startIdx + endIdx) / 2); 174 | mergeSortHelper(auxiliaryArray, startIdx, middleIdx, mainArray, animations); 175 | mergeSortHelper(auxiliaryArray, middleIdx + 1, endIdx, mainArray, animations); 176 | doMerge(mainArray, startIdx, middleIdx, endIdx, auxiliaryArray, animations); 177 | } 178 | 179 | function doMerge( 180 | mainArray, 181 | startIdx, 182 | middleIdx, 183 | endIdx, 184 | auxiliaryArray, 185 | animations, 186 | ) { 187 | let k = startIdx; 188 | let i = startIdx; 189 | let j = middleIdx + 1; 190 | while (i <= middleIdx && j <= endIdx) { 191 | // These are the values that we're comparing; we push them once 192 | // to change their color. 193 | animations.push([i, j]); 194 | // These are the values that we're comparing; we push them a second 195 | // time to revert their color. 196 | animations.push([i, j]); 197 | if (auxiliaryArray[i] <= auxiliaryArray[j]) { 198 | // We overwrite the value at index k in the original array with the 199 | // value at index i in the auxiliary array. 200 | animations.push([k, auxiliaryArray[i]]); 201 | mainArray[k++] = auxiliaryArray[i++]; 202 | } else { 203 | // We overwrite the value at index k in the original array with the 204 | // value at index j in the auxiliary array. 205 | animations.push([k, auxiliaryArray[j]]); 206 | mainArray[k++] = auxiliaryArray[j++]; 207 | } 208 | } 209 | while (i <= middleIdx) { 210 | // These are the values that we're comparing; we push them once 211 | // to change their color. 212 | animations.push([i, i]); 213 | // These are the values that we're comparing; we push them a second 214 | // time to revert their color. 215 | animations.push([i, i]); 216 | // We overwrite the value at index k in the original array with the 217 | // value at index i in the auxiliary array. 218 | animations.push([k, auxiliaryArray[i]]); 219 | mainArray[k++] = auxiliaryArray[i++]; 220 | } 221 | while (j <= endIdx) { 222 | // These are the values that we're comparing; we push them once 223 | // to change their color. 224 | animations.push([j, j]); 225 | // These are the values that we're comparing; we push them a second 226 | // time to revert their color. 227 | animations.push([j, j]); 228 | // We overwrite the value at index k in the original array with the 229 | // value at index j in the auxiliary array. 230 | animations.push([k, auxiliaryArray[j]]); 231 | mainArray[k++] = auxiliaryArray[j++]; 232 | } 233 | } --------------------------------------------------------------------------------