├── public
├── robots.txt
├── favicon.ico
├── logo192.png
├── logo512.png
├── manifest.json
└── index.html
├── src
├── components
│ ├── sounds
│ │ ├── sound1.wav
│ │ └── sound2.wav
│ ├── assets
│ │ ├── Button.js
│ │ ├── Toggle.js
│ │ ├── Dropdown.js
│ │ └── Slider.js
│ ├── styles
│ │ ├── dropdown.css
│ │ ├── visualizer.css
│ │ ├── button.css
│ │ ├── toggle.css
│ │ └── slider.css
│ └── Visualizer.js
├── App.js
├── setupTests.js
├── App.test.js
├── App.css
├── index.css
├── reportWebVitals.js
└── index.js
├── .gitignore
├── package.json
└── README.md
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harsh-solanki21/sorting-visualizer/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harsh-solanki21/sorting-visualizer/HEAD/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harsh-solanki21/sorting-visualizer/HEAD/public/logo512.png
--------------------------------------------------------------------------------
/src/components/sounds/sound1.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harsh-solanki21/sorting-visualizer/HEAD/src/components/sounds/sound1.wav
--------------------------------------------------------------------------------
/src/components/sounds/sound2.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harsh-solanki21/sorting-visualizer/HEAD/src/components/sounds/sound2.wav
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import './App.css'
3 | import Visualizer from './components/Visualizer'
4 |
5 | function App() {
6 | return (
7 | <>
8 |
9 | >
10 | )
11 | }
12 |
13 | export default App
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/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/components/assets/Button.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Button = ({ type, name, onClick, disabled }) => {
4 | return (
5 |
12 | )
13 | }
14 |
15 | export default Button
16 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | box-sizing: border-box;
5 | background-color: #f1f1f1;
6 | }
7 |
8 | @import url('./components/styles/button.css');
9 |
10 | @import url('./components/styles/dropdown.css');
11 |
12 | @import url('./components/styles/slider.css');
13 |
14 | @import url('./components/styles/toggle.css');
15 |
16 | @import url('./components/styles/visualizer.css');
17 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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/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/components/styles/dropdown.css:
--------------------------------------------------------------------------------
1 | .dropdown {
2 | background-color: white;
3 | border: 2px solid dodgerblue;
4 | border-radius: 5px;
5 | width: 10%;
6 | }
7 | .dropdown select:hover {
8 | cursor: pointer;
9 | }
10 | .dropdown select {
11 | font-size: 1rem;
12 | font-weight: normal;
13 | padding: 10px 15px 10px 2px;
14 | border: none;
15 | background-color: transparent;
16 | }
17 | .dropdown select:focus {
18 | outline: none;
19 | }
20 |
--------------------------------------------------------------------------------
/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/components/assets/Toggle.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Toggle = ({ context, soundState, onChange, disabled }) => {
4 | return (
5 |
6 |
{context}
7 |
8 |
12 | {soundState}
13 |
14 |
15 | )
16 | }
17 |
18 | export default Toggle
19 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/components/assets/Dropdown.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Dropdown = ({ onChange, disabled }) => {
4 | return (
5 |
6 |
14 |
15 | )
16 | }
17 |
18 | export default Dropdown
19 |
--------------------------------------------------------------------------------
/src/components/styles/visualizer.css:
--------------------------------------------------------------------------------
1 | .header {
2 | position: relative;
3 | padding: 0 25px;
4 | display: flex;
5 | flex-direction: row;
6 | align-items: center;
7 | justify-content: center;
8 | justify-content: space-between;
9 | background-color: aqua;
10 | }
11 |
12 | .sortingBars {
13 | position: absolute;
14 | top: 25%;
15 | left: 5%;
16 | }
17 |
18 | .bars {
19 | width: 10px;
20 | background-color: #ff7f50;
21 | margin-left: 4px;
22 | display: inline-block;
23 | border-radius: 10px;
24 | }
25 |
26 | .algoInfo {
27 | position: absolute;
28 | bottom: 30px;
29 | left: 5%;
30 | width: 90%;
31 | height: 70px;
32 | border: 3px solid #6495ed;
33 | border-radius: 5px;
34 | display: flex;
35 | justify-content: center;
36 | align-items: center;
37 | }
38 |
39 | .algoInfo div {
40 | margin-left: 30px;
41 | color: #ff581c;
42 | font-size: 25px;
43 | font-weight: 800;
44 | }
45 |
--------------------------------------------------------------------------------
/src/components/styles/button.css:
--------------------------------------------------------------------------------
1 | .button {
2 | color: white;
3 | border: none;
4 | font-size: 1rem;
5 | font-weight: 600;
6 | padding: 12px 15px;
7 | text-align: center;
8 | cursor: pointer;
9 | outline: none;
10 | border-radius: 5px;
11 | }
12 |
13 | .sort {
14 | background-color: #ea4c89;
15 | box-shadow: 0 2px #f082ac;
16 | }
17 |
18 | .sort:hover {
19 | background-color: #e7387b;
20 | box-shadow: 0 12px 16px 0 rgba(0, 0, 0, 0.24),
21 | 0 17px 50px 0 rgba(0, 0, 0, 0.19);
22 | }
23 |
24 | .sort:active {
25 | box-shadow: 0 2px #f082ac;
26 | transform: translateY(2px);
27 | }
28 |
29 | .newArray {
30 | background-color: dodgerblue;
31 | box-shadow: 0 2px powderblue;
32 | }
33 |
34 | .newArray:hover {
35 | background-color: #2a75ff;
36 | box-shadow: 0 12px 16px 0 rgba(0, 0, 0, 0.24),
37 | 0 17px 50px 0 rgba(0, 0, 0, 0.19);
38 | }
39 |
40 | .button:active {
41 | box-shadow: 0 2px powderblue;
42 | transform: translateY(2px);
43 | }
44 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sorting-visualizer",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.2",
7 | "@testing-library/react": "^12.1.4",
8 | "@testing-library/user-event": "^13.5.0",
9 | "react": "^17.0.2",
10 | "react-dom": "^17.0.2",
11 | "react-scripts": "5.0.0",
12 | "react-toggle": "^4.1.2",
13 | "use-sound": "^4.0.1",
14 | "web-vitals": "^2.1.4"
15 | },
16 | "scripts": {
17 | "start": "react-scripts start",
18 | "build": "react-scripts build",
19 | "test": "react-scripts test",
20 | "eject": "react-scripts eject"
21 | },
22 | "eslintConfig": {
23 | "extends": [
24 | "react-app",
25 | "react-app/jest"
26 | ]
27 | },
28 | "browserslist": {
29 | "production": [
30 | ">0.2%",
31 | "not dead",
32 | "not op_mini all"
33 | ],
34 | "development": [
35 | "last 1 chrome version",
36 | "last 1 firefox version",
37 | "last 1 safari version"
38 | ]
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/components/assets/Slider.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Slider = ({ onChange, disabled }) => {
4 | return (
5 |
38 | )
39 | }
40 |
41 | export default Slider
42 |
--------------------------------------------------------------------------------
/src/components/styles/toggle.css:
--------------------------------------------------------------------------------
1 | .toggle {
2 | text-align: center;
3 | }
4 |
5 | .context {
6 | float: left;
7 | padding-left: 7px;
8 | font-size: 1.1rem;
9 | font-weight: 400;
10 | }
11 |
12 | .sound-state {
13 | padding-left: 4px;
14 | }
15 |
16 | .toggle-switch {
17 | position: relative;
18 | display: inline-block;
19 | width: 60px;
20 | height: 34px;
21 | }
22 |
23 | .toggle-switch input {
24 | opacity: 0;
25 | width: 0;
26 | height: 0;
27 | }
28 |
29 | .slider {
30 | position: absolute;
31 | cursor: pointer;
32 | top: 0;
33 | left: 0;
34 | right: 0;
35 | bottom: 0;
36 | background-color: #ccc;
37 | -webkit-transition: 0.4s;
38 | transition: 0.4s;
39 | }
40 |
41 | .slider:before {
42 | position: absolute;
43 | content: '';
44 | height: 26px;
45 | width: 26px;
46 | left: 4px;
47 | bottom: 4px;
48 | background-color: white;
49 | -webkit-transition: 0.4s;
50 | transition: 0.4s;
51 | }
52 |
53 | input:checked + .slider {
54 | background-color: #31b975;
55 | }
56 |
57 | input:focus + .slider {
58 | box-shadow: 0 0 1px #31b975;
59 | }
60 |
61 | input:checked + .slider:before {
62 | -webkit-transform: translateX(26px);
63 | -ms-transform: translateX(26px);
64 | transform: translateX(26px);
65 | }
66 |
67 | .slider.round {
68 | border-radius: 34px;
69 | }
70 |
71 | .slider.round:before {
72 | border-radius: 50%;
73 | }
74 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🌈 Sorting Visualizer
2 |
3 | - This is a visualization tool for sorting algorithms made using React JS.
4 | - I build this project because I fascinated by the sorting algorithms and wanted to visualize them in action.
5 | - It also help others to comprehend the internal working of various sorting algorithms.
6 |
7 | ## Implemented Sorting Algorithms
8 |
9 | - Bubble Sort O(n2)
10 | - Selection Sort O(n2)
11 | - Insertion Sort O(n2)
12 | - Merge Sort O(n log n)
13 | - Quick Sort O(n log n)
14 | - Heap Sort O(n log n)
15 |
16 | ## Color Codes
17 |
18 | 🟧 `Randomized Array`
19 | 🟥 and 🟦 `Key comparisons`
20 | 🟩 `Sorted Array`
21 |
22 | ## Sorting Visualizer Interface
23 | > ## Landing Interface
24 | 
25 |
26 |
27 |
28 | > ## Sorted Array
29 | 
30 |
31 |
32 |
33 |
34 | ## To run this app locally
35 | - Download or clone this repo
36 | - In the project directory, run:
37 | #### `npm install`
38 | then
39 | #### `npm start`
40 | Runs the app in the development mode.\
41 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
42 |
43 | The page will reload when you make changes.\
44 | You may also see any lint errors in the console.
45 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/components/styles/slider.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | width: 30%;
3 | display: flex;
4 | flex-direction: column;
5 | align-items: center;
6 | }
7 |
8 | form {
9 | width: 90%;
10 | max-width: 500px;
11 | }
12 |
13 | .slider-title {
14 | font-size: 1.2rem;
15 | font-weight: 400;
16 | text-align: center;
17 | }
18 |
19 | form .speed-slider {
20 | display: flex;
21 | flex-direction: row;
22 | align-content: stretch;
23 | position: relative;
24 | width: 100%;
25 | height: 50px;
26 | user-select: none;
27 | }
28 |
29 | form .speed-slider::before {
30 | content: ' ';
31 | position: absolute;
32 | height: 2px;
33 | width: 100%;
34 | width: calc(100% * (2 / 3));
35 | top: 50%;
36 | left: 50%;
37 | transform: translate(-50%, -50%);
38 | background: #000;
39 | }
40 |
41 | form .speed-slider input,
42 | form .speed-slider label {
43 | box-sizing: border-box;
44 | flex: 1;
45 | user-select: none;
46 | cursor: pointer;
47 | }
48 |
49 | form .speed-slider label {
50 | display: inline-block;
51 | position: relative;
52 | width: 20%;
53 | height: 100%;
54 | user-select: none;
55 | }
56 |
57 | form .speed-slider label::before {
58 | content: attr(current-speed);
59 | position: absolute;
60 | left: 50%;
61 | padding-top: 10px;
62 | transform: translate(-50%, 45px);
63 | font-size: 14px;
64 | letter-spacing: 0.4px;
65 | font-weight: 400;
66 | white-space: nowrap;
67 | opacity: 0.85;
68 | transition: all 0.15s ease-in-out;
69 | }
70 |
71 | form .speed-slider label::after {
72 | content: ' ';
73 | position: absolute;
74 | left: 50%;
75 | top: 50%;
76 | transform: translate(-50%, -50%);
77 | width: 30px;
78 | height: 30px;
79 | border: 2px solid #000;
80 | background: #fff;
81 | border-radius: 50%;
82 | pointer-events: none;
83 | user-select: none;
84 | z-index: 1;
85 | cursor: pointer;
86 | transition: all 0.15s ease-in-out;
87 | }
88 |
89 | form .speed-slider label:hover::after {
90 | transform: translate(-50%, -50%) scale(1.25);
91 | }
92 |
93 | form .speed-slider input {
94 | display: none;
95 | }
96 |
97 | form .speed-slider input:checked + label::before {
98 | font-weight: 800;
99 | opacity: 1;
100 | }
101 |
102 | form .speed-slider input:checked + label::after {
103 | border-width: 4px;
104 | transform: translate(-50%, -50%) scale(0.75);
105 | }
106 |
107 | form .speed-slider input:checked ~ .slider-position {
108 | opacity: 1;
109 | }
110 |
111 | form .speed-slider input:checked:nth-child(1) ~ .slider-position {
112 | left: 16.7%;
113 | }
114 |
115 | form .speed-slider input:checked:nth-child(3) ~ .slider-position {
116 | left: 50%;
117 | }
118 |
119 | form .speed-slider input:checked:nth-child(5) ~ .slider-position {
120 | left: 83.3%;
121 | }
122 |
123 | form .speed-slider .slider-position {
124 | display: block;
125 | position: absolute;
126 | top: 50%;
127 | width: 12px;
128 | height: 12px;
129 | background: #000;
130 | border-radius: 50%;
131 | transition: all 0.15s ease-in-out;
132 | transform: translate(-50%, -50%);
133 | border: 2px solid #fff;
134 | opacity: 0;
135 | z-index: 2;
136 | }
137 |
138 | form:valid .speed-slider input + label::before {
139 | transform: translate(-50%, 45px) scale(0.9);
140 | transition: all 0.15s linear;
141 | }
142 |
143 | form:valid .speed-slider input:checked + label::before {
144 | transform: translate(-50%, 45px) scale(1.1);
145 | transition: all 0.15s linear;
146 | }
147 |
148 | form + button {
149 | display: block;
150 | position: relative;
151 | margin: 56px auto 0;
152 | padding: 10px 20px;
153 | appearance: none;
154 | transition: all 0.15s ease-in-out;
155 | font-family: inherit;
156 | font-size: 24px;
157 | font-weight: 600;
158 | background: #fff;
159 | border: 2px solid #000;
160 | border-radius: 8px;
161 | outline: 0;
162 | user-select: none;
163 | cursor: pointer;
164 | }
165 |
166 | form + button:hover {
167 | background: #000;
168 | color: #fff;
169 | }
170 |
171 | form + button:hover:active {
172 | transform: scale(0.9);
173 | }
174 |
175 | form + button:focus {
176 | background: #4caf50;
177 | border-color: #4caf50;
178 | color: #fff;
179 | pointer-events: none;
180 | }
181 |
182 | form + button:focus::before {
183 | animation: spin 1s linear infinite;
184 | }
185 |
186 | form + button::before {
187 | display: inline-block;
188 | width: 0;
189 | opacity: 0;
190 | content: '\f3f4';
191 | font-family: 'Font Awesome 5 Pro';
192 | font-weight: 900;
193 | margin-right: 0;
194 | transform: rotate(0deg);
195 | }
196 |
197 | form:invalid + button {
198 | pointer-events: none;
199 | opacity: 0.25;
200 | }
201 |
202 | @keyframes spin {
203 | from {
204 | transform: rotate(0deg);
205 | width: 24px;
206 | opacity: 1;
207 | margin-right: 12px;
208 | }
209 | to {
210 | transform: rotate(360deg);
211 | width: 24px;
212 | opacity: 1;
213 | margin-right: 12px;
214 | }
215 | }
216 |
--------------------------------------------------------------------------------
/src/components/Visualizer.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react'
2 | import useSound from 'use-sound'
3 | import sound1 from './sounds/sound1.wav'
4 | import sound2 from './sounds/sound2.wav'
5 | import Button from './assets/Button'
6 | import Dropdown from './assets/Dropdown'
7 | import Slider from './assets/Slider'
8 | import Toggle from './assets/Toggle'
9 |
10 | const ARRAYSIZE = 100
11 |
12 | const Visualizer = () => {
13 | const [primaryArray, setPrimaryArray] = useState([])
14 | const [algorithm, setAlgorithm] = useState('bubbleSort')
15 | const [animationSpeed, setAnimationSpeed] = useState(50)
16 | const [soundOn, setSoundOn] = useState(true)
17 | const [disableOptions, setDisableOptions] = useState(false)
18 | const [playBeep1] = useSound(sound1, { volume: soundOn ? 0.15 : 0 })
19 | const [playBeep2] = useSound(sound2, { volume: soundOn ? 0.05 : 0 })
20 |
21 | const randomizeArray = () => {
22 | for (let i = 0; i < primaryArray.length; i++) {
23 | let bar = document.getElementById(i).style
24 | bar.backgroundColor = '#ff7f50'
25 | }
26 | let array = []
27 | for (let i = 0; i < ARRAYSIZE; i++) {
28 | array.push(randomVals(20, 400))
29 | }
30 |
31 | setPrimaryArray(array)
32 | }
33 |
34 | const randomVals = (min, max) => {
35 | let randomVal = Math.floor(Math.random() * (max - min + 1) + min)
36 | return randomVal
37 | }
38 |
39 | useEffect(() => {
40 | randomizeArray()
41 | }, [])
42 |
43 | const sleep = (milliSeconds) => {
44 | return new Promise((resolve) => setTimeout(resolve, milliSeconds))
45 | }
46 |
47 | const finishedAnimation = async () => {
48 | for (let i = 0; i < primaryArray.length; i++) {
49 | let bar = document.getElementById(i).style
50 | bar.backgroundColor = 'green'
51 | playBeep1()
52 | await sleep(animationSpeed)
53 | }
54 | setDisableOptions(false)
55 | }
56 |
57 | const handleSorting = () => {
58 | setDisableOptions(true)
59 | switch (algorithm) {
60 | case 'bubbleSort':
61 | bubbleSort()
62 | break
63 | case 'selectionSort':
64 | selectionSort()
65 | break
66 | case 'insertionSort':
67 | insertionSort()
68 | break
69 | case 'mergeSort':
70 | mergeSort()
71 | break
72 | case 'quickSort':
73 | quickSort()
74 | break
75 | case 'heapSort':
76 | heapSort()
77 | break
78 | default:
79 | break
80 | }
81 | }
82 |
83 | // ------------ ALGORITHMS ------------ //
84 |
85 | // Bubble Sort
86 | const bubbleSort = async () => {
87 | let currentArr = primaryArray
88 | let sorted = false
89 | setAlgorithm({ name: 'Bubble Sort', timeComplexity: 'O(n^2)' })
90 |
91 | while (!sorted) {
92 | sorted = true
93 |
94 | for (let i = 0; i < currentArr.length - 1; i++) {
95 | for (let j = 0; j < currentArr.length - i - 1; j++) {
96 | if (currentArr[j] > currentArr[j + 1]) {
97 | let temp = currentArr[j]
98 | currentArr[j] = currentArr[j + 1]
99 | currentArr[j + 1] = temp
100 | setPrimaryArray([...primaryArray, currentArr])
101 |
102 | let bar1 = document.getElementById(j).style
103 | let bar2 = document.getElementById(j + 1).style
104 | bar1.backgroundColor = '#DC143C'
105 | bar2.backgroundColor = '#6A5ACD'
106 |
107 | await sleep(animationSpeed)
108 |
109 | bar1.backgroundColor = '#FF7F50'
110 | bar2.backgroundColor = '#FF7F50'
111 |
112 | sorted = false
113 | playBeep1()
114 | }
115 | }
116 | playBeep2()
117 | }
118 | if (sorted) finishedAnimation()
119 | }
120 | }
121 |
122 | // Selection Sort
123 | const selectionSort = async () => {
124 | let currentArr = primaryArray
125 | let sorted = false
126 | setAlgorithm({ name: 'Selection Sort', timeComplexity: 'O(n^2)' })
127 |
128 | while (!sorted) {
129 | sorted = true
130 |
131 | for (let i = 0; i < currentArr.length - 1; i++) {
132 | for (let j = i + 1; j < currentArr.length; j++) {
133 | if (currentArr[i] > currentArr[j]) {
134 | let swap1 = currentArr[i]
135 | let swap2 = currentArr[j]
136 | currentArr[i] = swap2
137 | currentArr[j] = swap1
138 | setPrimaryArray([...primaryArray, currentArr])
139 |
140 | let bar1 = document.getElementById(i).style
141 | let bar2 = document.getElementById(j).style
142 | bar1.backgroundColor = '#DC143C'
143 | bar2.backgroundColor = '#6A5ACD'
144 |
145 | await sleep(animationSpeed)
146 |
147 | bar1.backgroundColor = '#FF7F50'
148 | bar2.backgroundColor = '#FF7F50'
149 |
150 | sorted = false
151 | playBeep1()
152 | }
153 | }
154 | playBeep2()
155 | }
156 | if (sorted) finishedAnimation()
157 | }
158 | }
159 |
160 | // Insertion Sort
161 | const insertionSort = async () => {
162 | let currentArr = primaryArray
163 | let sorted = false
164 | setAlgorithm({ name: 'Insertion Sort', timeComplexity: 'O(n^2)' })
165 |
166 | while (!sorted) {
167 | sorted = true
168 |
169 | for (let i = 1; i < currentArr.length; i++) {
170 | let current = currentArr[i]
171 | let j = i - 1
172 | while (j >= 0 && currentArr[j] > current) {
173 | currentArr[j + 1] = currentArr[j]
174 | setPrimaryArray([...primaryArray, currentArr])
175 |
176 | let bar1 = document.getElementById(j + 1).style
177 | let bar2 = document.getElementById(j).style
178 | bar1.backgroundColor = '#DC143C'
179 | bar2.backgroundColor = '#6A5ACD'
180 |
181 | await sleep(animationSpeed)
182 |
183 | bar1.backgroundColor = '#FF7F50'
184 | bar2.backgroundColor = '#FF7F50'
185 |
186 | j--
187 | sorted = false
188 | playBeep1()
189 | }
190 | currentArr[j + 1] = current
191 | setPrimaryArray([...primaryArray, currentArr])
192 | playBeep2()
193 | }
194 | if (sorted) finishedAnimation()
195 | }
196 | }
197 |
198 | // Merge Sort
199 | const mergeSort = async () => {
200 | let currentArr = primaryArray
201 | setAlgorithm({ name: 'Merge Sort', timeComplexity: 'O(n log(n))' })
202 |
203 | await sort(currentArr, 0, currentArr.length - 1)
204 | finishedAnimation()
205 | }
206 |
207 | const sort = async (arr, low, high) => {
208 | if (low < high) {
209 | let mid = Math.floor((low + high) / 2)
210 | await sort(arr, low, mid)
211 | await sort(arr, mid + 1, high)
212 | await merge(arr, low, mid, high)
213 | }
214 | }
215 |
216 | const merge = async (arr, low, mid, high) => {
217 | let i = low
218 | let j = mid + 1
219 | let k = 0
220 | let tempArr = []
221 |
222 | while (i <= mid && j <= high) {
223 | if (arr[i] < arr[j]) {
224 | tempArr[k] = arr[i]
225 | i++
226 | k++
227 | } else {
228 | tempArr[k] = arr[j]
229 | j++
230 | k++
231 | }
232 | setPrimaryArray([...primaryArray, tempArr])
233 |
234 | let bar1 = document.getElementById(i).style
235 | let bar2 = document.getElementById(j).style
236 | bar1.backgroundColor = '#DC143C'
237 | bar2.backgroundColor = '#6A5ACD'
238 |
239 | await sleep(animationSpeed)
240 |
241 | bar1.backgroundColor = '#FF7F50'
242 | bar2.backgroundColor = '#FF7F50'
243 |
244 | playBeep1()
245 | }
246 |
247 | while (i <= mid) {
248 | tempArr[k] = arr[i]
249 |
250 | setPrimaryArray([...primaryArray, tempArr])
251 |
252 | let bar1 = document.getElementById(i).style
253 | let bar2 = document.getElementById(j).style
254 | bar1.backgroundColor = '#DC143C'
255 | bar2.backgroundColor = '#6A5ACD'
256 |
257 | await sleep(animationSpeed)
258 |
259 | bar1.backgroundColor = '#FF7F50'
260 | bar2.backgroundColor = '#FF7F50'
261 |
262 | playBeep1()
263 |
264 | i++
265 | k++
266 | }
267 |
268 | while (j <= high) {
269 | tempArr[k] = arr[j]
270 |
271 | setPrimaryArray([...primaryArray, tempArr])
272 |
273 | let bar1 = document.getElementById(i).style
274 | let bar2 = document.getElementById(j).style
275 | bar1.backgroundColor = '#DC143C'
276 | bar2.backgroundColor = '#6A5ACD'
277 |
278 | await sleep(animationSpeed)
279 |
280 | bar1.backgroundColor = '#FF7F50'
281 | bar2.backgroundColor = '#FF7F50'
282 |
283 | playBeep1()
284 |
285 | j++
286 | k++
287 | }
288 |
289 | for (let i = low; i <= high; i++) {
290 | arr[i] = tempArr[i - low]
291 | setPrimaryArray([...primaryArray, arr])
292 | playBeep2()
293 | }
294 | }
295 |
296 | // Quick Sort
297 | const quickSort = async () => {
298 | setAlgorithm({ name: 'Quick Sort', timeComplexity: 'O(n log(n))' })
299 | let currentArr = primaryArray
300 |
301 | await sorts(currentArr, 0, currentArr.length - 1)
302 | finishedAnimation()
303 | }
304 |
305 | const sorts = async (arr, left, right) => {
306 | if (left < right) {
307 | let partitionIndex = partition(arr, left, right)
308 |
309 | setPrimaryArray([...primaryArray, arr])
310 | await sleep(animationSpeed)
311 | playBeep2()
312 | await sorts(arr, left, partitionIndex - 1)
313 | await sorts(arr, partitionIndex + 1, right)
314 | }
315 | }
316 |
317 | const partition = (arr, left, right) => {
318 | let pivot = arr[right]
319 | let i = left - 1
320 | playBeep1()
321 | for (let j = left; j < right; j++) {
322 | if (arr[j] < pivot) {
323 | i++
324 | let temp = arr[i]
325 | arr[i] = arr[j]
326 | arr[j] = temp
327 |
328 | let bar1 = document.getElementById(i).style
329 | let bar2 = document.getElementById(j).style
330 | bar1.backgroundColor = '#DC143C'
331 | bar2.backgroundColor = '#6A5ACD'
332 |
333 | setTimeout(() => {
334 | bar1.backgroundColor = '#ff7f50'
335 | bar2.backgroundColor = '#ff7f50'
336 | }, 200)
337 |
338 | setPrimaryArray([...primaryArray, arr])
339 | }
340 | }
341 |
342 | let temp = arr[i + 1]
343 | arr[i + 1] = arr[right]
344 | arr[right] = temp
345 |
346 | return i + 1
347 | }
348 |
349 | // Heap Sort
350 | const heapSort = async () => {
351 | let arr = primaryArray
352 | let length = arr.length
353 | let index = Math.floor(length / 2 - 1)
354 | let lastChild = length - 1
355 |
356 | setAlgorithm({ name: 'Heap Sort', timeComplexity: 'O(n log(n))' })
357 |
358 | while (index >= 0) {
359 | await heapify(arr, length, index)
360 | index--
361 |
362 | setPrimaryArray([...primaryArray, arr])
363 |
364 | if (index >= 0) {
365 | let bar1 = document.getElementById(index).style
366 | let bar2 = document.getElementById(index + 1).style
367 | bar1.backgroundColor = '#DC143C'
368 | bar2.backgroundColor = '#6A5ACD'
369 |
370 | await sleep(animationSpeed)
371 |
372 | playBeep1()
373 |
374 | bar1.backgroundColor = '#FF7F50'
375 | bar2.backgroundColor = '#FF7F50'
376 | } else {
377 | await sleep(animationSpeed)
378 | }
379 | }
380 |
381 | while (lastChild >= 0) {
382 | let swap1 = arr[0]
383 | let swap2 = arr[lastChild]
384 |
385 | arr[0] = swap2
386 | arr[lastChild] = swap1
387 | await heapify(arr, lastChild, 0)
388 | lastChild--
389 | playBeep2()
390 |
391 | setPrimaryArray([...primaryArray, arr])
392 |
393 | if (index >= 0) {
394 | let bar1 = document.getElementById(lastChild).style
395 | let bar2 = document.getElementById(0).style
396 | bar1.backgroundColor = '#DC143C'
397 | bar2.backgroundColor = '#6A5ACD'
398 |
399 | bar1.backgroundColor = '#FF7F50'
400 | bar2.backgroundColor = '#FF7F50'
401 | } else {
402 | await sleep(animationSpeed)
403 | }
404 | }
405 |
406 | finishedAnimation()
407 | }
408 |
409 | const heapify = async (arr, length, index) => {
410 | let largest = index
411 | let leftNode = index * 2 + 1
412 | let rightNode = leftNode + 1
413 |
414 | if (arr[leftNode] > arr[largest] && leftNode < length) {
415 | largest = leftNode
416 | }
417 |
418 | if (arr[rightNode] > arr[largest] && rightNode < length) {
419 | largest = rightNode
420 | }
421 |
422 | if (largest !== index) {
423 | let swap1 = arr[index]
424 | let swap2 = arr[largest]
425 | arr[index] = swap2
426 | arr[largest] = swap1
427 |
428 | let bar1 = document.getElementById(index).style
429 | let bar2 = document.getElementById(largest).style
430 | bar1.backgroundColor = '#DC143C'
431 | bar2.backgroundColor = '#6A5ACD'
432 |
433 | await sleep(animationSpeed)
434 |
435 | bar1.backgroundColor = '#FF7F50'
436 | bar2.backgroundColor = '#FF7F50'
437 |
438 | playBeep1()
439 |
440 | await heapify(arr, length, largest)
441 | }
442 |
443 | return arr
444 | }
445 |
446 | return (
447 |
448 |
449 |
455 | setAlgorithm(e.target.value)}
457 | disabled={disableOptions}
458 | />
459 | setAnimationSpeed(e.target.value)}
461 | disabled={disableOptions}
462 | />
463 | {
468 | setSoundOn(!soundOn)
469 | }}
470 | disabled={disableOptions}
471 | />
472 |
478 |
479 |
480 | {primaryArray &&
481 | primaryArray.map((val, key) => {
482 | return (
483 |
489 | )
490 | })}
491 |
492 | {algorithm.name !== undefined && (
493 |
494 |
Algorithm: {algorithm.name}
495 |
Time Complexity: {algorithm.timeComplexity}
496 |
497 | )}
498 |
499 | )
500 | }
501 |
502 | export default Visualizer
503 |
--------------------------------------------------------------------------------