From 1262484ff117e4fd82994d89c291b2397ce35927 Mon Sep 17 00:00:00 2001 From: Madhu Mohan Reddy <128889553+ymadhumohanreddy@users.noreply.github.com> Date: Wed, 2 Oct 2024 10:57:22 +0530 Subject: [PATCH] Add files via upload --- README.md | 63 +++++++++++++++ app.js | 95 ++++++++++++++++++++++ helper.js | 47 +++++++++++ index.html | 81 +++++++++++++++++++ sort-algorithms.js | 162 +++++++++++++++++++++++++++++++++++++ style.css | 197 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 645 insertions(+) create mode 100644 README.md create mode 100644 app.js create mode 100644 helper.js create mode 100644 index.html create mode 100644 sort-algorithms.js create mode 100644 style.css diff --git a/README.md b/README.md new file mode 100644 index 0000000..9f0b6dd --- /dev/null +++ b/README.md @@ -0,0 +1,63 @@ + + +--- + +# Sorting Visualizer + +This is a **Sorting Visualizer** project built using **HTML**, **CSS**, and **JavaScript**. It provides a visual representation of different sorting algorithms, allowing users to see how various algorithms operate step-by-step on randomly generated arrays. + +## Features + +- **Sorting Algorithms**: + - Bubble Sort + - Selection Sort + - Insertion Sort + - Merge Sort + - Quick Sort +- **Custom Array Size**: Users can adjust the size of the array. +- **Speed Control**: Control the visualization speed of the sorting process. +- **Generate New Array**: Users can generate a new random array to visualize the sorting algorithms on different datasets. + + + + +## Project Structure + +- **`index.html`**: The main HTML file that defines the structure of the webpage. +- **`styles.css`**: The CSS file for styling the visualizer, layout, buttons, and overall appearance. +- **`app.js`**: The core JavaScript file containing the logic for sorting algorithms and the visualization. + +## Installation + +1. Clone the repository: + ```bash + git clone https://github.com/yourusername/sorting-visualizer.git + ``` +2. Open the project folder: + ```bash + cd sorting-visualizer + ``` +3. Open `index.html` in any modern web browser: + ```bash + open index.html + ``` + +## Usage + +1. Open the `index.html` file in your browser. +2. Use the controls on the page to: + - **Generate a new array**: Click the "Generate New Array" button. + - **Choose an algorithm**: Select a sorting algorithm from the dropdown. + - **Adjust array size**: Use the slider to change the size of the array. + - **Control speed**: Use the speed slider to slow down or speed up the visualization. + - **Start sorting**: Click on the "Sort" button to begin visualizing the sorting process. + +## Technologies Used + +- **HTML**: For structuring the page and elements. +- **CSS**: For styling the interface and array bars. +- **JavaScript**: For implementing the sorting algorithms and controlling the visualization. + + + +--- diff --git a/app.js b/app.js new file mode 100644 index 0000000..cd103a6 --- /dev/null +++ b/app.js @@ -0,0 +1,95 @@ +"use strict"; +const start = async () => { + let algoValue = Number(document.querySelector(".algo-menu").value); + let speedValue = Number(document.querySelector(".speed-menu").value); + + if (speedValue === 0) { + speedValue = 1; + } + if (algoValue === 0) { + alert("No Algorithm Selected"); + return; + } + + let algorithm = new sortAlgorithms(speedValue); + if (algoValue === 1) await algorithm.BubbleSort(); + if (algoValue === 2) await algorithm.SelectionSort(); + if (algoValue === 3) await algorithm.InsertionSort(); + if (algoValue === 4) await algorithm.MergeSort(); + if (algoValue === 5) await algorithm.QuickSort(); +}; + +const RenderScreen = async () => { + let algoValue = Number(document.querySelector(".algo-menu").value); + await RenderList(); +}; + +const RenderList = async () => { + let sizeValue = Number(document.querySelector(".size-menu").value); + await clearScreen(); + + let list = await randomList(sizeValue); + const arrayNode = document.querySelector(".array"); + console.log(arrayNode); + console.log(list); + for (const element of list) { + const node = document.createElement("div"); + node.className = "cell"; + node.setAttribute("value", String(element)); + node.style.height = `${3.8 * element}px`; + arrayNode.appendChild(node); + } +}; + +const RenderArray = async (sorted) => { + let sizeValue = Number(document.querySelector(".size-menu").value); + await clearScreen(); + + let list = await randomList(sizeValue); + if (sorted) list.sort((a, b) => a - b); + + const arrayNode = document.querySelector(".array"); + const divnode = document.createElement("div"); + divnode.className = "s-array"; + + for (const element of list) { + const dnode = document.createElement("div"); + dnode.className = "s-cell"; + dnode.innerText = element; + divnode.appendChild(dnode); + } + arrayNode.appendChild(divnode); +}; + +const randomList = async (Length) => { + let list = new Array(); + let lowerBound = 1; + let upperBound = 100; + + for (let counter = 0; counter < Length; ++counter) { + let randomNumber = Math.floor( + Math.random() * (upperBound - lowerBound + 1) + lowerBound + ); + list.push(parseInt(randomNumber)); + } + return list; +}; + +const clearScreen = async () => { + document.querySelector(".array").innerHTML = ""; +}; + +const response = () => { + let Navbar = document.querySelector(".navbar"); + if (Navbar.className === "navbar") { + Navbar.className += " responsive"; + } else { + Navbar.className = "navbar"; + } +}; + +document.querySelector(".icon").addEventListener("click", response); +document.querySelector(".start").addEventListener("click", start); +document.querySelector(".size-menu").addEventListener("change", RenderScreen); +document.querySelector(".algo-menu").addEventListener("change", RenderScreen); +window.onload = RenderScreen; diff --git a/helper.js b/helper.js new file mode 100644 index 0000000..49f3b47 --- /dev/null +++ b/helper.js @@ -0,0 +1,47 @@ +"use strict"; +class Helper { + constructor(time, list = []) { + this.time = parseInt(400/time); + this.list = list; + } + + mark = async (index) => { + this.list[index].setAttribute("class", "cell current"); + } + + markSpl = async (index) => { + this.list[index].setAttribute("class", "cell min"); + } + + unmark = async (index) => { + this.list[index].setAttribute("class", "cell"); + } + + pause = async() => { + return new Promise(resolve => { + setTimeout(() => { + resolve(); + }, this.time); + }); + } + + compare = async (index1, index2) => { + await this.pause(); + let value1 = Number(this.list[index1].getAttribute("value")); + let value2 = Number(this.list[index2].getAttribute("value")); + if(value1 > value2) { + return true; + } + return false; + } + + swap = async (index1, index2) => { + await this.pause(); + let value1 = this.list[index1].getAttribute("value"); + let value2 = this.list[index2].getAttribute("value"); + this.list[index1].setAttribute("value", value2); + this.list[index1].style.height = `${3.8*value2}px`; + this.list[index2].setAttribute("value", value1); + this.list[index2].style.height = `${3.8*value1}px`; + } +}; diff --git a/index.html b/index.html new file mode 100644 index 0000000..d0edb92 --- /dev/null +++ b/index.html @@ -0,0 +1,81 @@ + + + + + + + + + + + + + Sorting visualizer + + + +
+
+
+ + + + + + \ No newline at end of file diff --git a/sort-algorithms.js b/sort-algorithms.js new file mode 100644 index 0000000..eec8eaf --- /dev/null +++ b/sort-algorithms.js @@ -0,0 +1,162 @@ +"use strict"; +class sortAlgorithms { + constructor(time) { + this.list = document.querySelectorAll(".cell"); + this.size = this.list.length; + this.time = time; + this.help = new Helper(this.time, this.list); + } + + // BUBBLE SORT + BubbleSort = async () => { + for(let i = 0 ; i < this.size - 1 ; ++i) { + for(let j = 0 ; j < this.size - i - 1 ; ++j) { + await this.help.mark(j); + await this.help.mark(j+1); + if(await this.help.compare(j, j+1)) { + await this.help.swap(j, j+1); + } + await this.help.unmark(j); + await this.help.unmark(j+1); + } + this.list[this.size - i - 1].setAttribute("class", "cell done"); + } + this.list[0].setAttribute("class", "cell done"); + } + + // INSERTION SORT + InsertionSort = async () => { + for(let i = 0 ; i < this.size - 1 ; ++i) { + let j = i; + while(j >= 0 && await this.help.compare(j, j+1)) { + await this.help.mark(j); + await this.help.mark(j+1); + await this.help.pause(); + await this.help.swap(j, j+1); + await this.help.unmark(j); + await this.help.unmark(j+1); + j -= 1; + } + } + for(let counter = 0 ; counter < this.size ; ++counter) { + this.list[counter].setAttribute("class", "cell done"); + } + } + + // SELECTION SORT + SelectionSort = async () => { + for(let i = 0 ; i < this.size ; ++i) { + let minIndex = i; + for(let j = i ; j < this.size ; ++j) { + await this.help.markSpl(minIndex); + await this.help.mark(j); + if(await this.help.compare(minIndex, j)) { + await this.help.unmark(minIndex); + minIndex = j; + } + await this.help.unmark(j); + await this.help.markSpl(minIndex); + } + await this.help.mark(minIndex); + await this.help.mark(i); + await this.help.pause(); + await this.help.swap(minIndex, i); + await this.help.unmark(minIndex); + this.list[i].setAttribute("class", "cell done"); + } + } + + // MERGE SORT + MergeSort = async () => { + await this.MergeDivider(0, this.size - 1); + for(let counter = 0 ; counter < this.size ; ++counter) { + this.list[counter].setAttribute("class", "cell done"); + } + } + + MergeDivider = async (start, end) => { + if(start < end) { + let mid = start + Math.floor((end - start)/2); + await this.MergeDivider(start, mid); + await this.MergeDivider(mid+1, end); + await this.Merge(start, mid, end); + } + } + + Merge = async (start, mid, end) => { + let newList = new Array(); + let frontcounter = start; + let midcounter = mid + 1; + + while(frontcounter <= mid && midcounter <= end) { + let fvalue = Number(this.list[frontcounter].getAttribute("value")); + let svalue = Number(this.list[midcounter].getAttribute("value")); + if(fvalue >= svalue) { + newList.push(svalue); + ++midcounter; + } + else { + newList.push(fvalue); + ++frontcounter; + } + } + while(frontcounter <= mid) { + newList.push(Number(this.list[frontcounter].getAttribute("value"))); + ++frontcounter; + } + while(midcounter <= end) { + newList.push(Number(this.list[midcounter].getAttribute("value"))); + ++midcounter; + } + + for(let c = start ; c <= end ; ++c) { + this.list[c].setAttribute("class", "cell current"); + } + for(let c = start, point = 0 ; c <= end && point < newList.length; + ++c, ++point) { + await this.help.pause(); + this.list[c].setAttribute("value", newList[point]); + this.list[c].style.height = `${3.5*newList[point]}px`; + } + for(let c = start ; c <= end ; ++c) { + this.list[c].setAttribute("class", "cell"); + } + } + + // QUICK SORT + QuickSort = async () => { + await this.QuickDivider(0, this.size-1); + for(let c = 0 ; c < this.size ; ++c) { + this.list[c].setAttribute("class", "cell done"); + } + } + + QuickDivider = async (start, end) => { + if(start < end) { + let pivot = await this.Partition(start, end); + await this.QuickDivider(start, pivot-1); + await this.QuickDivider(pivot+1, end); + } + } + + Partition = async (start, end) => { + let pivot = this.list[end].getAttribute("value"); + let prev_index = start - 1; + + await this.help.markSpl(end); + for(let c = start ; c < end ; ++c) { + let currValue = Number(this.list[c].getAttribute("value")); + await this.help.mark(c); + if(currValue < pivot) { + prev_index += 1; + await this.help.mark(prev_index); + await this.help.swap(c, prev_index); + await this.help.unmark(prev_index); + } + await this.help.unmark(c); + } + await this.help.swap(prev_index+1, end); + await this.help.unmark(end); + return prev_index + 1; + } +}; \ No newline at end of file diff --git a/style.css b/style.css new file mode 100644 index 0000000..20ef4e4 --- /dev/null +++ b/style.css @@ -0,0 +1,197 @@ +:root { + --primary-color:#66b3c1; + --secondary-color: #30abc1; +} + +* { + /* Below is the standard CSS code one should add to get rid of default margins and padding which most of tge HTML elements have */ + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: "Roboto", sans-serif; + user-select: none; +} + +body { + position: relative; + min-height: 100vh; + text-align: center; + display: flex; + justify-content: space-between; + flex-direction: column; + align-items: stretch; +} + +/* Title CSS */ +.title { + background-color: var(--primary-color); + text-align: center; + font-size: 1.2em; + padding-block: 0.5em; + color:#fff; + cursor: pointer; +} + +/* Navbar CSS */ +.navbar { + display: flex; + justify-content: center; + align-items: center; + flex-wrap: wrap; + gap: 0.8em; + font-size: 16px; + min-height: 70px; + padding-block: 0.6em; + background-color: var(--secondary-color); + transition: all 0.5s cubic-bezier(0.645, 0.045, 0.355, 1); +} + +.navbar a { + all: unset; + cursor: pointer; + color: #fff; + font-weight: bold; + padding: 8px 10px; + border-radius: 6px; + transition: 0.3s; + background-color:#66b3c1; +} + +.navbar a:hover { + background-color: transparent; +} + +.navbar #menu { + width: fit-content; + outline: none; + border: none; + border-radius: 4px; + padding: 6px 8px; + background-color: #66b3c1; + color: white; +} + +.navbar>.icon { + display: none; +} + +#menu, +#random, +#start { + cursor: pointer; +} + +/* Center css */ +.center { + margin: 0 auto; + box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px; + /* Added shadow to make page border free */ + height: 420px; + width: 410px; + max-height: 731px; +} + +.array { + display: flex; + align-items: flex-start; + min-height: 100%; + height: 100%; + padding: 1rem; + flex-direction: row; +} + +.cell { + display: flex; + align-items: flex-start; + flex: 0.5; + width: 0.000001%; + margin: 1px; + background-color: #d6d6d6; + resize: horizontal; + position: relative; + transition: all 0.4s ease-in; +} + +.cell.done { + background-color: #9cec5b; + border-color: #9cec5b; + color: white; + transition: all 0.4s ease-out; +} + +.cell.visited { + border-color: #6184d8; + background-color: #6184d8; + color: white; + transition: 0.5s; +} + +.cell.current { + border-color: #50c5b7; + background-color: #50c5b7; + color: white; + transition: all 0.4s ease-out; +} + +.cell.min { + background-color: #ff1493; + border-color: #ff1493; + color: white; + transition: all 0.4s ease-out; +} + +/* Footer CSS */ +.fa.fa-heart { + color: #eb2c13; +} + +footer { + text-align: center; + font-size: 18px; + color: #2c3e50; + padding: 1.6em; +} + +.footer>p:nth-child(1) { + margin-bottom: 0.6em; +} + +.link { + text-decoration: none; + font-weight: bold; + color: #ff5252; + font-size: 20px; +} + +@media screen and (max-width: 600px) { + .navbar { + gap: 0.4em; + } + + .title { + font-size: 17px; + } + + .navbar *, + .navbar a { + font-size: 14px; + } + + .footer { + font-size: 18px; + } + + a#random { + order: 4; + } + + a.start { + order: 5; + } +} + +@media screen and (max-width: 550px) { + .center { + width: 95%; + } +} \ No newline at end of file