├── .gitignore ├── .vscode └── launch.json ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt └── src ├── App.css ├── App.js ├── App.test.js ├── controller └── SortingVisualizer.jsx ├── index.css ├── index.js ├── logo.svg ├── model ├── BubbleSort.js ├── HeapSort.js ├── HelperFunctions.js ├── InsertionSort.js ├── MergeSort.js └── QuickSort.js ├── reportWebVitals.js ├── setupTests.js └── view └── SortingVisualizer.css /.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 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "pwa-chrome", 9 | "request": "launch", 10 | "name": "Launch Chrome against localhost", 11 | "url": "http://localhost:8080", 12 | "webRoot": "${workspaceFolder}" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Welcome to my sorting visualizer! This project implement a beautiful visualizer for 5 of my favorite sorting algorithms using React framework. You can find all important code in src/model, view, controller. 2 | 3 | # To access the visualizer 4 | http://xuqianzhi.github.io/Sorting-Visualizer. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sorting_visualizer", 3 | "homepage": "http://xuqianzhi.github.io/Sorting-Visualizer", 4 | "version": "0.1.0", 5 | "private": true, 6 | "dependencies": { 7 | "@testing-library/jest-dom": "^5.12.0", 8 | "@testing-library/react": "^11.2.7", 9 | "@testing-library/user-event": "^12.8.3", 10 | "react": "^17.0.2", 11 | "react-dom": "^17.0.2", 12 | "react-scripts": "4.0.3", 13 | "web-vitals": "^1.1.2" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject", 20 | "predeploy": "npm run build", 21 | "deploy": "gh-pages -d build" 22 | }, 23 | "eslintConfig": { 24 | "extends": [ 25 | "react-app", 26 | "react-app/jest" 27 | ] 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.2%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | }, 41 | "devDependencies": { 42 | "gh-pages": "^3.2.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqianzhi/Sorting-Visualizer/c140cac54a21338d7a2e671f2e767d8a676b1364/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | Sorting Visualizer 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqianzhi/Sorting-Visualizer/c140cac54a21338d7a2e671f2e767d8a676b1364/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqianzhi/Sorting-Visualizer/c140cac54a21338d7a2e671f2e767d8a676b1364/public/logo512.png -------------------------------------------------------------------------------- /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 './App.css'; 3 | import SortingVisualizer from './controller/SortingVisualizer' 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/controller/SortingVisualizer.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, useState } from 'react'; 2 | import reactDom from 'react-dom'; 3 | import '../view/SortingVisualizer.css' 4 | import {mergeSortAnimation} from '../model/MergeSort.js' 5 | import {quickSortAnimation} from '../model/QuickSort.js' 6 | import {insertionSortAnimation} from '../model/InsertionSort.js' 7 | import {bubbleSortAnimation} from '../model/BubbleSort.js' 8 | import {heapSortAnimation} from '../model/HeapSort.js' 9 | import {randomIntFromInterval, isArraySorted} from '../model/HelperFunctions.js' 10 | 11 | const ANIMATION_LAG = 50; 12 | const MAIN_BAR_COLOR = "rgb(187, 145, 248)"; 13 | const CHANGING_BAR_COLOR = "rgb(252, 242, 81)" 14 | const SUPPORTING_COLOR = "darkorange" 15 | const FINISHED_COLOR = "mediumaquamarine" 16 | const RUNNING_STATUS = Object.freeze({'running': 'running', 'pausing': 'pausing', 'not_running': 'not_running'}) 17 | 18 | export default class SortingVisualizer extends Component { 19 | 20 | constructor(props) { 21 | super(props); 22 | this.state = { 23 | bars: [], 24 | bar_num: 100, //determines bar num, min: 8, max: 200; 25 | running_status: RUNNING_STATUS.not_running, 26 | animation_speed: 2, 27 | timeoutIDs: [], //for clearTimeout 28 | bar_change_animation: [], //for pausing and continuing animation 29 | animation_begin_at: 0 //for pausing and continuing animation 30 | } 31 | } 32 | 33 | componentDidMount() { 34 | this.resetBars(); 35 | } 36 | 37 | resetBars() { 38 | const buf = []; 39 | const curr_displayed_bars = document.getElementsByClassName("bar"); 40 | for (let i = 0; i < this.state.bar_num; i++) { 41 | buf.push(randomIntFromInterval(5, 300)); 42 | if (curr_displayed_bars[i]) { 43 | curr_displayed_bars[i].style.backgroundColor = MAIN_BAR_COLOR; 44 | } 45 | } 46 | this.setState({bars: buf}); 47 | } 48 | 49 | displayAnimation(bar_change_animation, animation_begin_at) { 50 | const curr_displayed_bars = document.getElementsByClassName("bar"); 51 | const animation_speed = 1 / (2**(this.state.animation_speed)); 52 | var timeoutIDs = []; 53 | for (let i = animation_begin_at; i < bar_change_animation.length; i++) { 54 | const changing_bar_index = bar_change_animation[i][0]; 55 | const new_bar_height = bar_change_animation[i][1]; 56 | let supporting_index = null; 57 | if (bar_change_animation[i].length == 3) { 58 | supporting_index = bar_change_animation[i][2]; 59 | } 60 | 61 | // begin animation 62 | const currTimeoutID = setTimeout(() => { 63 | this.setState({running_status: RUNNING_STATUS.running}); 64 | if (i != 0) { 65 | const prev_changing_bar_index = bar_change_animation[i - 1][0]; 66 | curr_displayed_bars[prev_changing_bar_index].style.backgroundColor = MAIN_BAR_COLOR; 67 | if (bar_change_animation[i - 1].length == 3) { 68 | const prev_supporting_index = bar_change_animation[i - 1][2]; 69 | curr_displayed_bars[prev_supporting_index].style.backgroundColor = MAIN_BAR_COLOR; 70 | } 71 | } 72 | let buf = this.state.bars; 73 | buf[changing_bar_index] = new_bar_height; 74 | this.setState({bars: buf}); 75 | if (supporting_index) { // supporting index exist 76 | curr_displayed_bars[supporting_index].style.backgroundColor = SUPPORTING_COLOR; 77 | } 78 | curr_displayed_bars[changing_bar_index].style.backgroundColor = CHANGING_BAR_COLOR; 79 | this.setState({animation_begin_at: i}) // keep track of current index for pausing and continuing 80 | }, ANIMATION_LAG * animation_speed * (i - animation_begin_at)); 81 | timeoutIDs.push(currTimeoutID); 82 | } 83 | 84 | // animation stopped 85 | const id1 = setTimeout(() => { 86 | this.setState({animation_begin_at: 0, bar_change_animation: []}); 87 | document.getElementById('pause-button').style.display = 'none'; 88 | document.getElementById('stop-button').style.display = 'none'; 89 | for (let i = 0; i < this.state.bar_num; i++) { 90 | curr_displayed_bars[i].style.backgroundColor = FINISHED_COLOR; 91 | } 92 | }, ANIMATION_LAG * animation_speed * (bar_change_animation.length - animation_begin_at)); 93 | const id2 = setTimeout(() => { 94 | this.setState({running_status: RUNNING_STATUS.not_running}); 95 | }, ANIMATION_LAG * animation_speed * (bar_change_animation.length - animation_begin_at) + 1000); 96 | const id3 = setTimeout(() => { 97 | this.resetBars(); 98 | }, ANIMATION_LAG * animation_speed * (bar_change_animation.length - animation_begin_at) + 1500); 99 | 100 | timeoutIDs.push(id1); 101 | timeoutIDs.push(id2); 102 | timeoutIDs.push(id3); 103 | this.setState({timeoutIDs, timeoutIDs}); 104 | } 105 | 106 | sortAndDisplay(method) { 107 | /* 108 | method type: String 109 | method cases: merge, quick, insertion, bubble, heap 110 | */ 111 | const lst = this.state.bars.slice(); 112 | var bar_change_animation = []; 113 | if (method === "merge") { 114 | mergeSortAnimation(lst, 0, lst.length, bar_change_animation); 115 | } else if (method === "quick") { 116 | quickSortAnimation(lst, 0, lst.length, bar_change_animation); 117 | } else if (method === "insertion") { 118 | insertionSortAnimation(lst, bar_change_animation); 119 | } else if (method === "bubble") { 120 | bubbleSortAnimation(lst, bar_change_animation); 121 | } else if (method === "heap") { 122 | heapSortAnimation(lst, bar_change_animation); 123 | } else { 124 | return; 125 | } 126 | this.displayAnimation(bar_change_animation, 0); 127 | this.setState({bar_change_animation: bar_change_animation}); 128 | } 129 | 130 | barNumSliderChanged(event) { 131 | const val = parseInt(event.target.value); 132 | const buf = []; 133 | const curr_displayed_bars = document.getElementsByClassName("bar"); 134 | for (let i = 0; i < val; i++) { 135 | buf.push(randomIntFromInterval(5, 300)); 136 | if (curr_displayed_bars[i]) { 137 | curr_displayed_bars[i].style.backgroundColor = MAIN_BAR_COLOR; 138 | } 139 | } 140 | this.setState({bar_num: val, bars: buf}); 141 | } 142 | 143 | speedSliderChanged(event) { 144 | const val = parseInt(event.target.value); 145 | this.setState({animation_speed: val}); 146 | } 147 | 148 | clearOngoingAnimation() { 149 | const timeoutIDs = this.state.timeoutIDs; 150 | if (timeoutIDs.length == 0) { 151 | return; 152 | } 153 | for (let i = 0; i < timeoutIDs.length; i++) { 154 | const currID = timeoutIDs[i]; 155 | clearTimeout(currID); 156 | } 157 | } 158 | 159 | pauseAnimation() { 160 | this.clearOngoingAnimation(); 161 | this.setState({running_status: RUNNING_STATUS.pausing, timeoutIDs: []}); 162 | } 163 | 164 | continueAnimation() { 165 | this.displayAnimation(this.state.bar_change_animation, this.state.animation_begin_at); 166 | } 167 | 168 | stopAndResetAnimation() { 169 | this.clearOngoingAnimation(); 170 | this.setState({timeoutIDs: [], bar_change_animation: [], animation_begin_at: 0, running_status: RUNNING_STATUS.not_running}); 171 | this.resetBars(); 172 | } 173 | 174 | render() { 175 | const bars = this.state.bars.slice(); 176 | const bar_num = this.state.bar_num; 177 | const running_status = this.state.running_status; 178 | const initial_button_position = 7; 179 | const animation_speed = 2**(this.state.animation_speed); 180 | return ( 181 |
182 |
183 | 185 | 187 | 189 | 191 | 193 |
194 | 195 | this.speedSliderChanged(event)}/> 203 |
204 | 205 | 211 | 212 | 217 | 223 | 224 |
225 | 226 |
227 | {bars.map((val, idx) => ( 228 |
237 |
238 | ))} 239 | this.barNumSliderChanged(event)}/> 247 |
248 |
249 | ) 250 | } 251 | } -------------------------------------------------------------------------------- /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/model/BubbleSort.js: -------------------------------------------------------------------------------- 1 | export function bubbleSortAnimation( 2 | lst, 3 | bar_change_animation) { 4 | /* 5 | lst: is the entire whole array 6 | bar_change_animation: keeps track all intermediate changes, 7 | 0th place is new index, 1st place is new height 8 | */ 9 | if (lst.length <= 1) { 10 | return; 11 | } 12 | while (true) { 13 | var somethingSwapped = false; 14 | for (let i = 0; i < lst.length - 1; i++) { 15 | const firstElem = lst[i]; 16 | const secondElem = lst[i + 1]; 17 | if (firstElem > secondElem) { 18 | lst[i] = secondElem; 19 | lst[i + 1] = firstElem; 20 | somethingSwapped = true; 21 | bar_change_animation.push([i, secondElem]); 22 | bar_change_animation.push([i + 1, firstElem]); 23 | } 24 | } 25 | if (!somethingSwapped) { 26 | break; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/model/HeapSort.js: -------------------------------------------------------------------------------- 1 | export function heapSortAnimation( 2 | lst, 3 | bar_change_animation) { 4 | /* 5 | lst: is the entire whole array 6 | start & end: the segment of the array we are sorting 7 | bar_change_animation: keeps track all intermediate changes, 8 | 0th place is new index, 1st place is new height 9 | */ 10 | var heap = new MinHeap(); 11 | for (let i = 0; i < lst.length; i++) { 12 | bar_change_animation.push([i, lst[i]]); 13 | heap.insert(lst[i]); 14 | } 15 | for (let i = 0; i < lst.length; i++) { 16 | const min_val = heap.extractMin(); 17 | lst[i] = min_val; 18 | bar_change_animation.push([i, min_val]); 19 | } 20 | } 21 | 22 | 23 | function MinHeap() { 24 | this.data = []; 25 | } 26 | 27 | MinHeap.prototype.insert = function(val) { 28 | this.data.push(val); 29 | this.bubbleUp(this.data.length-1); 30 | }; 31 | 32 | MinHeap.prototype.bubbleUp = function(index) { 33 | while (index > 0) { 34 | // get the parent 35 | var parent = Math.floor((index + 1) / 2) - 1; 36 | 37 | // if parent is greater than child 38 | if (this.data[parent] > this.data[index]) { 39 | // swap 40 | var temp = this.data[parent]; 41 | this.data[parent] = this.data[index]; 42 | this.data[index] = temp; 43 | } 44 | 45 | index = parent; 46 | } 47 | }; 48 | 49 | MinHeap.prototype.extractMin = function() { 50 | var min = this.data[0]; 51 | 52 | // set first element to last element 53 | this.data[0] = this.data.pop(); 54 | 55 | // call bubble down 56 | this.bubbleDown(0); 57 | 58 | return min; 59 | }; 60 | 61 | MinHeap.prototype.bubbleDown = function(index) { 62 | while (true) { 63 | var child = (index+1)*2; 64 | var sibling = child - 1; 65 | var toSwap = null; 66 | 67 | // if current is greater than child 68 | if (this.data[index] > this.data[child]) { 69 | toSwap = child; 70 | } 71 | 72 | // if sibling is smaller than child, but also smaller than current 73 | if (this.data[index] > this.data[sibling] && (this.data[child] == null || (this.data[child] !== null && this.data[sibling] < this.data[child]))) { 74 | toSwap = sibling; 75 | } 76 | 77 | // if we don't need to swap, then break. 78 | if (toSwap == null) { 79 | break; 80 | } 81 | 82 | var temp = this.data[toSwap]; 83 | this.data[toSwap] = this.data[index]; 84 | this.data[index] = temp; 85 | 86 | index = toSwap; 87 | } 88 | 89 | MinHeap.prototype.clear = function() { 90 | this.data = []; 91 | } 92 | }; 93 | -------------------------------------------------------------------------------- /src/model/HelperFunctions.js: -------------------------------------------------------------------------------- 1 | export function randomIntFromInterval(min, max) { 2 | // min and max included 3 | return Math.floor(Math.random() * (max - min + 1) + min); 4 | } 5 | 6 | export function isArraySorted(lst) { 7 | let prev_val = Number.MIN_SAFE_INTEGER; 8 | for (let i = 0; i < lst.length; i++) { 9 | const curr_val = lst[i]; 10 | if (curr_val < prev_val) { 11 | return false; 12 | } 13 | prev_val = curr_val; 14 | } 15 | return true; 16 | } -------------------------------------------------------------------------------- /src/model/InsertionSort.js: -------------------------------------------------------------------------------- 1 | export function insertionSortAnimation( 2 | lst, 3 | bar_change_animation) { 4 | /* 5 | lst: is the entire whole array 6 | start & end: the segment of the array we are sorting 7 | bar_change_animation: keeps track all intermediate changes, 8 | 0th place is new index, 1st place is new height 9 | */ 10 | for (let i = 1; i < lst.length; i++) { 11 | const inserting_elem = lst[i]; 12 | var j = i - 1; // j will be the inserting position 13 | var temp_animations = []; 14 | while(j >= 0 && inserting_elem < lst[j]) { 15 | lst[j + 1] = lst[j]; 16 | temp_animations.push([j + 1, lst[j]]); 17 | temp_animations.push([j, inserting_elem]); 18 | j -= 1; 19 | } 20 | for (let k = 0; k < temp_animations.length; k++) { 21 | var curr_animation = temp_animations[k]; 22 | curr_animation.push([j + 1]); 23 | bar_change_animation.push(curr_animation); 24 | } 25 | lst[j + 1] = inserting_elem; 26 | bar_change_animation.push([j + 1, lst[j + 1], j + 1]); 27 | } 28 | } -------------------------------------------------------------------------------- /src/model/MergeSort.js: -------------------------------------------------------------------------------- 1 | export function mergeSortAnimation( 2 | lst, 3 | start, 4 | end, 5 | bar_change_animation) { 6 | /* 7 | lst: is the entire whole array 8 | start & end: the segment of the array we are sorting 9 | bar_change_animation: keeps track all intermediate changes, 10 | 0th place is new index, 1st place is new height 11 | */ 12 | 13 | if (start + 1 >= end) { 14 | return; 15 | } 16 | const mid = Math.floor((start + end) / 2); 17 | mergeSortAnimation(lst, start, mid, bar_change_animation); 18 | mergeSortAnimation(lst, mid, end, bar_change_animation); 19 | let i = start; //left half index 20 | let j = mid; //right half index 21 | let k = start; //result list index 22 | let result = lst.slice(); 23 | while (i < mid || j < end) { 24 | let temp; 25 | if (i === mid) { 26 | while (k < end) { 27 | bar_change_animation.push([k, lst[j], j]); 28 | result[k] = lst[j]; 29 | k += 1; 30 | j += 1; 31 | } 32 | break; 33 | } 34 | if (j === end) { 35 | while (k < end) { 36 | bar_change_animation.push([k, lst[i], i]); 37 | result[k] = lst[i]; 38 | k += 1; 39 | i += 1; 40 | } 41 | break; 42 | } 43 | const leftElem = lst[i]; 44 | const rightElem = lst[j]; 45 | temp = result[k]; 46 | if (leftElem < rightElem) { 47 | bar_change_animation.push([k, leftElem, j]); 48 | result[k] = leftElem; 49 | i += 1; 50 | } else { 51 | bar_change_animation.push([k, rightElem, i]); 52 | result[k] = rightElem; 53 | j += 1; 54 | } 55 | k += 1; 56 | } 57 | for (let i = start; i < end; i++) { 58 | lst[i] = result[i]; 59 | } 60 | } -------------------------------------------------------------------------------- /src/model/QuickSort.js: -------------------------------------------------------------------------------- 1 | export function quickSortAnimation( 2 | lst, 3 | start, 4 | end, 5 | bar_change_animation) { 6 | /* 7 | This function pick the median of start and end index as the pivot 8 | lst: is the entire whole array 9 | start & end: the segment of the array we are sorting 10 | bar_change_animation: keeps track all intermediate changes, 11 | 0th: new index, 1st: new height, 2nd: pivot index 12 | */ 13 | if (start + 1 >= end) { 14 | return; 15 | } 16 | const pivot_index = randomIntFromInterval(start, end - 1); 17 | // const mid = Math.floor((start + end) / 2); 18 | 19 | const pivot_val = lst[pivot_index]; 20 | let leftHalf = []; 21 | let rightHalf = []; 22 | for (let i = start; i < end; i++) { 23 | if (i == pivot_index) {continue} 24 | const curr_val = lst[i]; 25 | curr_val <= pivot_val ? leftHalf.push(curr_val) : rightHalf.push(curr_val); 26 | } 27 | const new_pivot_index = leftHalf.length + start; 28 | leftHalf.push(pivot_val); 29 | const overall_list = leftHalf.concat(rightHalf); 30 | for (let i = start; i < end; i++) { 31 | lst[i] = overall_list[i - start]; 32 | bar_change_animation.push([i, lst[i], new_pivot_index]); 33 | } 34 | quickSortAnimation(lst, start, new_pivot_index, bar_change_animation); 35 | quickSortAnimation(lst, new_pivot_index + 1, end, bar_change_animation); 36 | } 37 | 38 | function randomIntFromInterval(min, max) { 39 | // min and max included 40 | return Math.floor(Math.random() * (max - min + 1) + min); 41 | } -------------------------------------------------------------------------------- /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/view/SortingVisualizer.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: rgb(30, 30, 30); 3 | } 4 | 5 | .button-container { 6 | background-color: rgb(61, 61, 61); 7 | height: 120px; 8 | position: relative; 9 | } 10 | 11 | .button { 12 | background-color: rgb(92, 91, 90); 13 | padding: 10px; 14 | margin: 10px; 15 | margin-top: 50px; 16 | width: 12%; 17 | border-width: 1px; 18 | border-radius: 12px; 19 | position: absolute; 20 | 21 | color: white; 22 | font-size: 16px; 23 | font-weight: bold; 24 | } 25 | 26 | .bar-container { 27 | margin: auto; 28 | width: 1000px; 29 | height: 430px; 30 | position: relative; 31 | background-color: transparent; 32 | } 33 | 34 | .bar { 35 | position: absolute; 36 | bottom: 0px; 37 | margin: 0 1px; 38 | border-top-left-radius: 10px; 39 | border-top-right-radius: 10px; 40 | border-bottom-left-radius: 6px; 41 | border-bottom-right-radius: 6px; 42 | } 43 | 44 | .side-container { 45 | width: 12%; 46 | height: 75px; 47 | position: absolute; 48 | right: 20px; 49 | margin-top: 35px; 50 | /* background-color: yellow; */ 51 | background-color:rgb(61, 61, 61); 52 | } 53 | 54 | .barnum-slider { 55 | margin-top: 50%; 56 | -webkit-appearance: none; 57 | height: 10px; 58 | width: 90%; 59 | background: #d3d3d3; 60 | opacity: 0.7; 61 | border-radius: 15px; 62 | } 63 | 64 | .barnum-slider::-webkit-slider-thumb { 65 | border-radius: 13px; 66 | appearance: none; 67 | width: 26px; 68 | height: 26px; 69 | background: rgb(187, 145, 248); 70 | } 71 | 72 | .speed-slider { 73 | margin-top: 27%; 74 | position: absolute; 75 | -webkit-appearance: none; 76 | height: 7px; 77 | width: 90%; 78 | background:white; 79 | opacity: 0.7; 80 | border-radius: 15px; 81 | } 82 | 83 | .speed-slider::-webkit-slider-thumb { 84 | border-radius: 9px; 85 | appearance: none; 86 | width: 18px; 87 | height: 18px; 88 | background: lightgrey; 89 | } --------------------------------------------------------------------------------