├── LICENSE ├── README.md ├── algos ├── bst │ ├── index.html │ ├── script.js │ └── styles.css ├── path_finding │ ├── index.html │ ├── script.js │ └── styles.css ├── sorting │ ├── index.html │ ├── script.js │ └── styles.css └── trie │ ├── index.html │ ├── script.js │ └── styles.css ├── assets ├── banner.webp ├── bst.jpg ├── grid.png ├── home-page.jpeg ├── path-preview.jpg ├── path.jpg ├── preview2.jpg ├── sorting-preview.jpg ├── sorting.jpg ├── trie-preview.jpg └── trie.jpg ├── css └── styles.css └── index.html /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Sayan Maity 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Algorithm Visualizer 2 | 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 4 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](http://makeapullrequest.com) 5 | [![Netlify Status](https://api.netlify.com/api/v1/badges/6adb0f19-ac48-45e2-a896-9f6ac6d2f620/deploy-status)](https://app.netlify.com/sites/sayancr777-algorithm-visualizer/deploys) 6 | 7 | >```Algorithm Visualizer``` is an interactive way and platform that visualize the algorithms in four main domain i.e. Trie data structure, Binary Search Tree, Path Finding and Sort Visual algorithm. The project focuses on visualizng 📊 the algorithm and try to make easier to understand and learn the algorithm. 8 | 9 | Author : [Sayan Maity](sayancr777@gmail.com) 10 | 11 | 12 | 13 | ## 🛠️ Built with : 14 | 15 | - **Frontend**: HTML, CSS, Javascript 16 | - **Version Control**: Git 17 | - **Hosting**: Netlify, Github Pages 18 | 19 | ## ✨ Features : 20 | - Step-by-step animation of the algorithm 21 | - Ability to pause animation 22 | - a short information on each algorithms and data structures 23 | 24 | ## 👨‍💻 How to Use : 25 | - Select the algorithm you want to visualize from the dropdown menu. 26 | - Enter the input for the algorithm in the text field (for trie and binary search tree, you can use a list of words) 27 | - Press the "Visualize" button to see the animation of the algorithm in action. 28 | - Press the "Pause" button to pause the animation 29 | - Now go and play with it 30 | 31 | ## 📒 Description : 32 | Will be added sonner ... 33 | 34 | 35 | ## 📸 Screenshots : 36 | 37 | 38 | 43 | 44 | 45 | 50 | 55 | 56 | 57 | 61 | 66 | 67 |
39 | 40 |
41 |

Home Page

42 |
46 | 47 |
48 |

Trie

49 |
51 | 52 |
53 |

Binary Search Tree

54 |
58 | 59 |
60 |

Path Finding

62 | 63 |
64 |

Sorting

65 |
68 | 69 | 70 | ## ⏳ Future Improvements : 71 | 72 | - [ ] Add more and more algorithms and data structures 73 | - [ ] Bring a lot of animations to help users visualize things more perfectly 74 | - [ ] Adding the time complexity chart and some small info on each topics 75 | 76 | ## Contributing 77 | 78 | We welcome contributions! If you have an idea for a new feature or have found a bug, please open an issue on Github. 79 | 80 | ## 📝 Endnote 81 | So if you have liked this project then do consider giving it a star which will encourage me to build more of this kind of projects in future and also if you want youcan follow me on [Github](https://github.com/Sayan-Maity/) 😊 82 | Keep Coding ! 83 | 84 |

~ Sayan Maity

85 | -------------------------------------------------------------------------------- /algos/bst/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Algorithm Visualizer | Binary Search Tree 5 | 6 | 7 | 8 |
9 | 18 |
19 |
20 |
21 |

Edge

22 |
23 |
24 |
25 |

Node

26 |
27 | 28 |
29 |
30 |
31 |
32 | 33 | 34 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /algos/bst/script.js: -------------------------------------------------------------------------------- 1 | // prerequisite: javascript, d3.js 2 | 3 | // structure of node 4 | // { 5 | // nodeId: number <--- unique id for each node. it will be also used as id for html element of node. 6 | // value: character <-- this will store value of current node. 7 | // children: [] <--- this array will store left and right of node 8 | // } 9 | 10 | // Tree will be stored as object. 11 | let data = { value: null, children: [] }; 12 | // Current available id for the node. We will give each node a unique id and put this as their html element "id". 13 | let curId = 1; 14 | 15 | const width = Math.max(100, window.innerWidth - 50); 16 | const height = Math.max(100, window.innerHeight - 100); 17 | const nodeRadius = 20; 18 | const LinkStroke = 4; 19 | const animationDuration = 750; 20 | const padding = 22; 21 | 22 | d3.select('.Canvas').append('svg').append('g'); 23 | 24 | // During insertion or deletion visualization process disbale the buttons 25 | const freezeButtons = () => { 26 | document.getElementById('InsertButton').disabled = true; 27 | document.getElementById('DeleteButton').disabled = true; 28 | }; 29 | const unfreezeButtons = () => { 30 | document.getElementById('InsertButton').disabled = false; 31 | document.getElementById('DeleteButton').disabled = false; 32 | }; 33 | 34 | // To put delay between visualization. 35 | const sleep = (ms) => { 36 | return new Promise((resolve) => setTimeout(resolve, ms)); 37 | }; 38 | 39 | const update = (oldData, newData, parentValue, childValue) => { 40 | // childValue is node we want to insert/delete and parentValue is parent of node we want to insert/delete. 41 | 42 | /* 43 | Small description of "update" function. 44 | -> Find the co-ordinates of old tree. 45 | -> Find the co-ordinates of new updated tree. 46 | -> Put tree on old co-ordinates. 47 | -> Animate nodes and links to the new co-ordinates. 48 | */ 49 | 50 | // Create the old and new updated tree. 51 | const treemap = d3.tree().size([width, height]); 52 | const oldTree = treemap(d3.hierarchy(data, (d) => d.children)); 53 | const newTree = treemap(d3.hierarchy(newData, (d) => d.children)); 54 | 55 | // Convert both tres from objects to array. 56 | const oldTreeArray = oldTree.descendants(); 57 | const newTreeArray = newTree.descendants(); 58 | // Putting old and new co-ordinates of nodes in the same array. 59 | for (let i = 0; i < newTreeArray.length; i++) { 60 | let oldPosition = {}; 61 | // Traverse the old tree and find the old co-ordinates. 62 | for (let j = 0; j < oldTreeArray.length; j++) { 63 | if (newTreeArray[i].data.value == childValue) { 64 | // Node which we are going to insert, there is no old co-oridnates available 65 | // So we are going to use the co-ordinates of parent node. 66 | if (oldTreeArray[j].data.value == parentValue) { 67 | oldPosition = oldTreeArray[j]; 68 | } 69 | } else { 70 | if (oldTreeArray[j].data.value == newTreeArray[i].data.value) { 71 | oldPosition = oldTreeArray[j]; 72 | } 73 | } 74 | } 75 | newTreeArray[i].oldX = oldPosition.x || 0; 76 | newTreeArray[i].oldY = (oldPosition.y || 0) + padding; 77 | newTreeArray[i].y += padding; 78 | } 79 | 80 | // Remove the old tree from canvas. we will draw new one. 81 | d3.select('.Canvas > svg g').remove(); 82 | d3.select('.Canvas > svg').append('g'); 83 | 84 | // Create all the edges and put them in new array. 85 | let allLinks = []; 86 | for (let i = 0; i < newTreeArray.length; i++) { 87 | for (let j = 0; j < 2; j++) { 88 | if (newTreeArray[i].data.value != null && newTreeArray[i].children[j].data.value != null) { 89 | allLinks.push({ 90 | parent: newTreeArray[i], 91 | child: newTreeArray[i].children[j], 92 | }); 93 | } 94 | } 95 | } 96 | 97 | // We will draw edge(links) 2 times. why? 98 | // When we traverse the BST, it will be easy to show animation of searching. we will paint top edge with some color while searching node. 99 | for (let i = 0; i < 2; i++) { 100 | const lineId = i == 0 ? 'Under' : ''; 101 | 102 | // Drawing edges on canvas with some styles and co-ordinates. 103 | const links = d3 104 | .select('.Canvas > svg g') 105 | .selectAll('g.link') 106 | .data(allLinks) 107 | .enter() 108 | .append('g') 109 | .append('line') 110 | .attr('id', (d) => `${lineId}link_Source_${d.parent.data.nodeId}_Dest_${d.child.data.nodeId}`) 111 | .attr('stroke-width', LinkStroke) 112 | .attr('stroke', 'black') 113 | .attr('x1', (d) => d.parent.oldX) 114 | .attr('y1', (d) => d.parent.oldY) 115 | .attr('x2', (d) => d.child.oldX) 116 | .attr('y2', (d) => d.child.oldY); 117 | // Transition from old tree to new tree. move old edges to new edges using co-ordinates. 118 | links 119 | .transition() 120 | .duration(animationDuration) 121 | .attr('x1', (d) => d.parent.x) 122 | .attr('y1', (d) => d.parent.y) 123 | .attr('x2', (d) => d.child.x) 124 | .attr('y2', (d) => d.child.y); 125 | } 126 | 127 | // Draw nodes and their value on screen using old tree co-ordinates. 128 | const nodes = d3 129 | .select('.Canvas > svg g') 130 | .selectAll('g.node') 131 | .data(newTree) 132 | .enter() 133 | .append('g') 134 | .attr('id', (d) => `node${d.data.nodeId}`) 135 | .attr('class', (d) => (d.data.value != null ? 'node' : 'null-node')); 136 | nodes 137 | .append('circle') 138 | .attr('id', (d) => `circle${d.data.nodeId}`) 139 | .attr('r', nodeRadius) 140 | .attr('cx', (d) => d.oldX) 141 | .attr('cy', (d) => d.oldY) 142 | .attr('value', (d) => d.data.value); 143 | nodes 144 | .append('text') 145 | .attr('dx', (d) => d.oldX) 146 | .attr('dy', (d) => d.oldY) 147 | .attr('text-anchor', 'middle') 148 | .attr('alignment-baseline', 'middle') 149 | .attr('font-size', '20px') 150 | .attr('font-weight', 'bold') 151 | .text((d) => d.data.value); 152 | 153 | // Move nodes from old co-ordinate to new co-ordinates. 154 | nodes 155 | .transition() 156 | .duration(animationDuration) 157 | .attr('transform', (d) => { 158 | if (d.data.value != null) return `translate(${parseInt(d.x - d.oldX)}, ${parseInt(d.y - d.oldY)})`; 159 | else return 'translate(0,0)'; 160 | }); 161 | 162 | data = newData; 163 | }; 164 | 165 | const addNode = async () => { 166 | // Get the node value from input field and verify it's value/type. 167 | let val = document.getElementById('InsertNodeField').value; 168 | if (val == '') { 169 | return; 170 | } 171 | if (isNaN(val)) { 172 | alert('Only integers values are allowed'); 173 | return; 174 | } 175 | val = parseInt(val); 176 | document.getElementById('InsertNodeField').value = ''; 177 | 178 | // Freeze(disable) insert and delete buttons. 179 | freezeButtons(); 180 | 181 | // Copying object without reference in a dirty way. Might make proper function to copy object later. 182 | let oldData = JSON.parse(JSON.stringify(data)); 183 | let newData = JSON.parse(JSON.stringify(data)); 184 | let node = newData; 185 | let parent = null; 186 | 187 | while (true) { 188 | if (node.value == null) { 189 | // If node value is null then that means we already reached leaf node. Insert new node here. 190 | await sleep(400); 191 | 192 | // Create a node with given valule. 193 | const newChild = { 194 | nodeId: curId, 195 | value: val, 196 | children: [{ value: null }, { value: null }], 197 | }; 198 | 199 | if (parent) { 200 | // If tree is not empty then "parent" will not be null. Now link newly created node with it parent node. 201 | if (parent.value < val) parent.children[1] = newChild; 202 | else parent.children[0] = newChild; 203 | } else { 204 | // If tree is empty then this newly created node will act as tree. 205 | newData = newChild; 206 | } 207 | 208 | update(oldData, newData, (parent ? parent.value : -1), (parent ? val : -1)); 209 | curId++; 210 | await sleep(300); 211 | break; 212 | } 213 | 214 | const nodeElement = document.getElementById(`node${node.nodeId}`); 215 | if (nodeElement) nodeElement.className.baseVal = 'highlightedNode'; 216 | 217 | if (node.value == val) { 218 | // If value user is trying to insert already exist then show message 219 | alert('Value already exists in tree'); 220 | update(oldData, oldData, -1, -1); 221 | break; 222 | } 223 | 224 | parent = node; 225 | // Go to left or right subtree depending on node values. 226 | if (node.value > val) { 227 | node = node.children[0]; 228 | } else { 229 | node = node.children[1]; 230 | } 231 | 232 | // Show the edge traversing animation. 233 | const linkElement = document.getElementById(`link_Source_${parent.nodeId}_Dest_${node.nodeId}`); 234 | if (linkElement) { 235 | linkElement.className.baseVal = 'LinkAnimation'; 236 | await sleep(750); 237 | } 238 | } 239 | unfreezeButtons(); 240 | }; 241 | 242 | // Delete the given node and return updated tree. 243 | const deleteNodeRecur = (newData, val) => { 244 | if (newData.value == null) { 245 | return newData; 246 | } 247 | 248 | if (val < newData.value) { 249 | // Find the node in left subtree and return updated subtree. 250 | newData.children[0] = deleteNodeRecur(newData.children[0], val); 251 | } else if (val > newData.value) { 252 | // Find the node in right subtree and return updated subtree. 253 | newData.children[1] = deleteNodeRecur(newData.children[1], val); 254 | } else { 255 | // Found the node which we want to delete. 256 | if (newData.children[0].value == null) { 257 | // There is no left children 258 | return newData.children[1]; 259 | } else if (newData.children[1].value == null) { 260 | // There is no right children 261 | return newData.children[0]; 262 | } 263 | 264 | // Both children Exists 265 | // In this case we will find inorder successor of node and replace it with current node. 266 | let successorParent = newData; 267 | let successor = newData.children[1]; 268 | while (successor.children[0].value != null) { 269 | successorParent = successor; 270 | successor = successor.children[0]; 271 | } 272 | if (successorParent.value != newData.value) successorParent.children[0] = successor.children[1]; 273 | else successorParent.children[1] = successor.children[1]; 274 | newData.value = successor.value; 275 | return newData; 276 | } 277 | return newData; 278 | }; 279 | 280 | const deleteNode = async () => { 281 | // Get the node value from input field and verify it's type. 282 | let val = document.getElementById('DeleteNodeField').value; 283 | if (val == '') return; 284 | if (isNaN(val)) { 285 | alert('Only integer values are allowed'); 286 | return; 287 | } 288 | val = parseInt(val); 289 | document.getElementById('DeleteNodeField').value = ''; 290 | freezeButtons(); 291 | 292 | // Copying object without reference in a dirty way. Might make proper function to copy object later. 293 | let oldData = JSON.parse(JSON.stringify(data)); 294 | let newData = JSON.parse(JSON.stringify(data)); 295 | let node = newData; 296 | let parent = null; 297 | 298 | while (true) { 299 | if (node.value == null) { 300 | alert('Value is not present in tree'); 301 | update(oldData, newData, -1, -1); 302 | break; 303 | } 304 | 305 | const nodeEle = document.getElementById(`node${node.nodeId}`); 306 | if (nodeEle) nodeEle.className.baseVal = 'highlightedNode'; 307 | 308 | parent = node; 309 | 310 | if (node.value == val) { 311 | // Found the node which we want to delete. We just create updated tree with some other function and display it. 312 | // More better way will be, if there are 2 childs to this node then show animation to find inorder successor. 313 | await sleep(500); 314 | newData = deleteNodeRecur(newData, val); 315 | update(oldData, newData, -1, -1); 316 | break; 317 | } else { 318 | // Go to left or right subtree depending on the value we want to delete. 319 | if (node.value > val) { 320 | node = node.children[0]; 321 | } else { 322 | node = node.children[1]; 323 | } 324 | // Show the edge aniamtion. 325 | const linkElement = document.getElementById(`link_Source_${parent.nodeId}_Dest_${node.nodeId}`); 326 | if (linkElement) linkElement.className.baseVal = 'LinkAnimation'; 327 | } 328 | await sleep(750); 329 | } 330 | unfreezeButtons(); 331 | }; 332 | 333 | document.getElementById('InsertButton').addEventListener('click', addNode); 334 | document.getElementById('DeleteButton').addEventListener('click', deleteNode); 335 | 336 | // If during writing in insertion or deletion input field, user presses enter key then click on insertion/deletion button. 337 | document.getElementById('InsertNodeField').addEventListener('keyup', function (event) { 338 | if (event.key === 'Enter') { 339 | document.getElementById('InsertButton').click(); 340 | } 341 | }); 342 | document.getElementById('DeleteNodeField').addEventListener('keyup', function (event) { 343 | if (event.key === 'Enter') { 344 | document.getElementById('DeleteButton').click(); 345 | } 346 | }); 347 | 348 | 349 | const init = async () => { 350 | const list = [15, 7, 25, 4, 10, 20, 30, 2, 6, 8, 13, 18, 22, 28, 35]; 351 | for (let i = 0; i < list.length; i++) { 352 | document.getElementById('InsertNodeField').value = list[i]; 353 | await addNode(); 354 | } 355 | }; 356 | // init(); 357 | -------------------------------------------------------------------------------- /algos/bst/styles.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap'); 2 | 3 | 4 | * { 5 | padding: 0; 6 | margin: 0; 7 | box-sizing: border-box; 8 | } 9 | 10 | body { 11 | background: #212833 ; 12 | box-shadow: inset 0 70px 80px rgb(0 0 0 / 21%); 13 | font-family: 'Poppins', sans-serif; 14 | 15 | } 16 | 17 | /* ============= Scrollbar ============= */ 18 | ::-webkit-scrollbar { 19 | width: 4px; 20 | } 21 | ::-webkit-scrollbar-track { 22 | background: #000; 23 | } 24 | ::-webkit-scrollbar-thumb { 25 | background: white; 26 | } 27 | 28 | 29 | 30 | .container { 31 | display: flex; 32 | flex-direction: column; 33 | justify-content: center; 34 | align-items: center; 35 | display: flex; 36 | 37 | } 38 | #header { 39 | width: 1400px; 40 | margin-top: 1rem; 41 | 42 | box-shadow: 10px 10px 30px rgba(0, 0, 0, 0.363); 43 | border-radius: 10px; 44 | background-color: rgba(255, 255, 255, 0.1); 45 | overflow: hidden; 46 | display: flex; 47 | flex-direction: row; 48 | justify-content: center; 49 | border-top: 1px solid rgba(255, 255, 255, 0.4); 50 | border-left: 1px solid rgba(255, 255, 255, 0.4); 51 | font-family: 'Poppins', sans-serif; 52 | backdrop-filter: blur(8px); 53 | } 54 | #header a { 55 | text-decoration: none; 56 | 57 | } 58 | #title { 59 | text-align: center; 60 | font-size: 2rem; 61 | /* color: rgb(90, 0, 90) ; */ 62 | color: rgb(244, 244, 244); 63 | padding: 1rem 0; 64 | font-weight: bold; 65 | } 66 | 67 | 68 | #OperationContainer { 69 | position: absolute; 70 | top: 25px; 71 | left: 20px; 72 | } 73 | 74 | #OperationContainer button { 75 | background: #161824; 76 | margin-left: 0.5rem; 77 | outline: none; 78 | border: none; 79 | padding: 0.4rem 1rem; 80 | color: #fff; 81 | border-radius: 2px; 82 | } 83 | #OperationContainer button:hover { 84 | cursor: pointer; 85 | } 86 | #OperationContainer input { 87 | padding-left: 0.4rem !important; 88 | padding: 0.3rem 0; 89 | border-radius: 2px; 90 | outline: none; 91 | border: 1px solid transparent; 92 | } 93 | #OperationContainer input:focus { 94 | border: 1px solid rgb(183, 0, 255); 95 | } 96 | 97 | 98 | #InsertNodeField, 99 | #DeleteNodeField { 100 | width: 120px; 101 | } 102 | #DeleteNodeField { 103 | margin-left: 10px; 104 | } 105 | 106 | .Canvas { 107 | width: 100%; 108 | height: 100vh; 109 | display: flex; 110 | overflow: hidden; 111 | margin-left: auto; 112 | margin-right: auto; 113 | position: relative; 114 | } 115 | 116 | 117 | .indicator_container { 118 | display: flex; 119 | align-items: center; 120 | justify-content: center; 121 | background-color: cornflowerblue; 122 | padding: 0.5rem 1rem ; 123 | font-size: 0.9rem; 124 | gap: 2rem; 125 | width: 800px; 126 | } 127 | 128 | .indicator { 129 | display: flex; 130 | align-items: center; 131 | } 132 | .indicator_block { 133 | margin-right: 0.3rem; 134 | } 135 | .indicator #edge{ 136 | height: 0.2rem; 137 | width: 2.5rem; 138 | background-color: #099691; 139 | box-shadow: 0 0 1px #333; 140 | } 141 | 142 | .indicator #node { 143 | height: 1.5rem; 144 | width: 1.5rem; 145 | border-radius: 50%; 146 | background-color: #25d003; 147 | box-shadow: 0 0 1px #333; 148 | } 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | .indicator text { 157 | font-size: 20px !important; 158 | } 159 | 160 | svg { 161 | margin-top: 5px; 162 | width: 100%; 163 | /* max-width: 1200px; */ 164 | height: 100%; 165 | overflow: scroll; 166 | } 167 | 168 | .node circle { 169 | fill: #29e703; 170 | /* fill-opacity: 0.6; */ 171 | stroke: #168700; 172 | stroke-width: 1px; 173 | } 174 | .node text { 175 | font-size: 22px; 176 | fill: #ffffff; 177 | } 178 | 179 | .highlightedNode circle { 180 | fill: red; 181 | stroke: red; 182 | stroke-width: 1px; 183 | } 184 | 185 | .null-node, 186 | .null-link { 187 | visibility: hidden; 188 | } 189 | 190 | line { 191 | stroke: #099b96; 192 | } 193 | 194 | .LinkAnimation { 195 | stroke: red; 196 | stroke-dasharray: 500; 197 | animation: filling 1s linear forwards; 198 | } 199 | 200 | @keyframes filling { 201 | from { 202 | stroke-dashoffset: 500; 203 | } 204 | to { 205 | stroke-dashoffset: 0; 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /algos/path_finding/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Algorithm Visualizer | Path Finder 11 | 12 | 13 | 26 | 27 |
28 |
29 |
30 |

Starting Position

31 |
32 |
33 |
34 |

Ending Position

35 |
36 |
37 |
38 |

Visited Node

39 |
40 |
41 |
42 |

Wall

43 |
44 |
45 |
46 |

Path

47 |
48 |
49 |
50 |
51 | 52 | 53 |
54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /algos/path_finding/script.js: -------------------------------------------------------------------------------- 1 | const rows = 19; 2 | const cols = 44; 3 | 4 | let startingPointX = 9; 5 | let startingPointY = 15; 6 | let endingPointX = 9; 7 | let endingPointY = 27; 8 | 9 | let speed = 10; 10 | 11 | // it will show whether visualization is currently in process or not. 12 | let currentlyRunning = false; 13 | let foundPath = false; 14 | let mouseIsDown = false; 15 | 16 | // for the grid "isBlocked" will show whether there is wall or not on given cell. 17 | let isBlocked = new Array(rows); 18 | let gridElements = new Array(rows); 19 | for (let i = 0; i < rows; i++) { 20 | isBlocked[i] = new Array(cols).fill(0); 21 | gridElements[i] = new Array(cols); 22 | } 23 | 24 | class PriorityQueue { 25 | constructor(defaultNode, comparator) { 26 | this.values = []; 27 | this.comparator = comparator; 28 | this.defaultNode = defaultNode; 29 | } 30 | // implemention of heap. based on "comparator" function heap will behave like min-heap or max-heap; 31 | add(element) { 32 | this.values.push(element); 33 | let index = this.values.length - 1; 34 | while (index >= 1 && this.comparator(this.values[index], this.values[Math.floor(index / 2)])) { 35 | [this.values[index], this.values[Math.floor(index / 2)]] = [ 36 | this.values[Math.floor(index / 2)], 37 | this.values[index], 38 | ]; 39 | index = Math.floor(index / 2); 40 | } 41 | } 42 | getTop() { 43 | if (this.isEmpty()) return 'No elements in priority queue'; 44 | return this.values[0]; 45 | } 46 | pop() { 47 | if (this.isEmpty()) return 'Underflow'; 48 | [this.values[0], this.values[this.values.length - 1]] = [this.values[this.values.length - 1], this.values[0]]; 49 | this.values.pop(); 50 | 51 | let element = this.values[0]; 52 | let index = 0; 53 | while (index * 2 + 1 < this.values.length) { 54 | let leftNode = this.defaultNode; 55 | let rightNode = this.defaultNode; 56 | if (index * 2 + 1 < this.values.length) leftNode = this.values[index * 2 + 1]; 57 | if (index * 2 + 2 < this.values.length) rightNode = this.values[index * 2 + 2]; 58 | 59 | if (this.comparator(element, rightNode) && this.comparator(element, leftNode)) break; 60 | if (index * 2 + 2 === this.values.length || this.comparator(leftNode, rightNode)) { 61 | [this.values[index], this.values[index * 2 + 1]] = [this.values[index * 2 + 1], this.values[index]]; 62 | index = index * 2 + 1; 63 | } else { 64 | [this.values[index], this.values[index * 2 + 2]] = [this.values[index * 2 + 2], this.values[index]]; 65 | index = index * 2 + 2; 66 | } 67 | } 68 | } 69 | isEmpty() { 70 | return this.values.length === 0; 71 | } 72 | } 73 | 74 | class Deque { 75 | constructor() { 76 | this.values = []; 77 | } 78 | push_back(element) { 79 | this.values.push(element); 80 | } 81 | pop_back() { 82 | if (this.isEmpty()) return; 83 | return this.values.pop(); 84 | } 85 | push_front(element) { 86 | this.values.unshift(element); 87 | } 88 | pop_front() { 89 | if (this.isEmpty()) return; 90 | return this.values.shift(); 91 | } 92 | isEmpty() { 93 | return this.values.length == 0; 94 | } 95 | } 96 | 97 | // create grid like struccture using table. 98 | const init = () => { 99 | const allowDrop = (e) => { 100 | e.preventDefault(); 101 | }; 102 | 103 | const drag = (e) => { 104 | mouseIsDown = false; 105 | e.dataTransfer.setData('sourceId', e.target.id); 106 | }; 107 | 108 | const drop = (e) => { 109 | e.preventDefault(); 110 | let sourceId = e.dataTransfer.getData('sourceId'); 111 | let targetId = e.target.id; 112 | 113 | var src = document.getElementById(sourceId); 114 | var tar = document.getElementById(targetId); 115 | let tarX = Number.parseInt(tar.id.substr(1, 2)); 116 | let tarY = Number.parseInt(tar.id.substr(4, 2)); 117 | if (!src || !tar) return; 118 | if ( 119 | (src.className === 'startPoint' && tar.className === 'endPoint') || 120 | (src.className === 'endPoint' && tar.className === 'startPoint') 121 | ) { 122 | [startingPointX, endingPointX] = [endingPointX, startingPointX]; 123 | [startingPointY, endingPointY] = [endingPointY, startingPointY]; 124 | } else { 125 | if (src.className === 'startPoint') { 126 | startingPointX = tarX; 127 | startingPointY = tarY; 128 | } else { 129 | endingPointX = tarX; 130 | endingPointY = tarY; 131 | } 132 | src.draggable = false; 133 | src.ondragstart = null; 134 | tar.draggable = true; 135 | tar.ondragstart = (e) => drag(e); 136 | } 137 | 138 | [src.className, tar.className] = [tar.className, src.className]; 139 | [src.id, tar.id] = [tar.id, src.id]; 140 | }; 141 | const createGrid = (rows, cols, callback) => { 142 | let grid = document.createElement('table'); 143 | grid.className = 'grid'; 144 | 145 | for (let i = 0; i < rows; i++) { 146 | let tr = grid.appendChild(document.createElement('tr')); 147 | tr.ondrop = (e) => drop(e); 148 | tr.ondragover = (e) => allowDrop(e); 149 | 150 | for (let j = 0; j < cols; j++) { 151 | let cell = tr.appendChild(document.createElement('td')); 152 | // Unique ID for each cell. if row or cell number is single digit then put 0 infront of it. 153 | cell.id = 'r' + ('0' + i).slice(-2) + 'c' + ('0' + j).slice(-2); 154 | 155 | if (i == startingPointX && j == startingPointY) { 156 | cell.className = 'startPoint'; 157 | cell.draggable = true; 158 | cell.ondragstart = (e) => drag(e); 159 | } else if (i == endingPointX && j == endingPointY) { 160 | cell.className = 'endPoint'; 161 | cell.draggable = true; 162 | cell.ondragstart = (e) => drag(e); 163 | } 164 | 165 | gridElements[i][j] = cell; 166 | 167 | cell.addEventListener( 168 | 'mouseover', 169 | () => { 170 | if (mouseIsDown) callback(cell, i, j); 171 | }, 172 | false 173 | ); 174 | 175 | cell.addEventListener( 176 | 'mousedown', 177 | () => { 178 | callback(cell, i, j); 179 | }, 180 | false 181 | ); 182 | } 183 | } 184 | 185 | return grid; 186 | }; 187 | const grid = createGrid(rows, cols, function (el, row, col) { 188 | if (row == startingPointX && col == startingPointY) { 189 | } else if (row == endingPointX && col == endingPointY) { 190 | } else if (el.className == 'clicked') { 191 | el.className = ''; 192 | isBlocked[row][col] = 0; 193 | } else { 194 | el.className = 'clicked'; 195 | isBlocked[row][col] = 1; 196 | } 197 | }); 198 | document.getElementById('container').appendChild(grid); 199 | document.querySelector('body').addEventListener('mousedown', () => { 200 | mouseIsDown = 1; 201 | }); 202 | document.querySelector('body').addEventListener('mouseup', () => { 203 | mouseIsDown = 0; 204 | }); 205 | }; 206 | init(); 207 | 208 | const isValid = (x, y) => { 209 | return x >= 0 && y >= 0 && x < rows && y < cols; 210 | }; 211 | // to put delay between visualization. 212 | function sleep(ms) { 213 | return new Promise((resolve) => setTimeout(resolve, ms)); 214 | } 215 | const animate = async (nodesToAnimate, speed) => { 216 | for (let i = 0; i < nodesToAnimate.length; i++) { 217 | gridElements[nodesToAnimate[i].x][nodesToAnimate[i].y].className = 'visited'; 218 | await sleep(speed); 219 | if (currentlyRunning == false) { 220 | clearGrid(); 221 | return; 222 | } 223 | } 224 | }; 225 | const generatePath = (path) => { 226 | let pathFromSrcToDest = []; 227 | let curi = endingPointX; 228 | let curj = endingPointY; 229 | 230 | while (!(curi == startingPointX && curj == startingPointY)) { 231 | if (path[curi][curj] == 'D') { 232 | curi++; 233 | } else if (path[curi][curj] == 'U') { 234 | curi--; 235 | } else if (path[curi][curj] == 'L') { 236 | curj--; 237 | } else if (path[curi][curj] == 'R') { 238 | curj++; 239 | } 240 | if (!(curi == startingPointX && curj == startingPointY)) { 241 | pathFromSrcToDest.push({ x: curi, y: curj }); 242 | } 243 | } 244 | pathFromSrcToDest.reverse(); 245 | return pathFromSrcToDest; 246 | }; 247 | const animatePath = async (path) => { 248 | if (currentlyRunning == false) return; 249 | let pathFromSrcToDest = generatePath(path); 250 | 251 | for (let i = 0; i < pathFromSrcToDest.length; i++) { 252 | let { x, y } = pathFromSrcToDest[i]; 253 | gridElements[x][y].className = 'path'; 254 | await sleep(speed * 2.5); 255 | 256 | if (currentlyRunning === false) { 257 | clearGrid(); 258 | return; 259 | } 260 | } 261 | currentlyRunning = false; 262 | }; 263 | 264 | const Dijkstra = async () => { 265 | // initiazing variables. 266 | const INF = 1000000000; 267 | let vis = new Array(rows); 268 | let path = new Array(rows); 269 | let dis = new Array(rows); 270 | for (let i = 0; i < rows; i++) { 271 | vis[i] = new Array(cols).fill(0); 272 | path[i] = new Array(cols).fill('0'); 273 | dis[i] = new Array(cols).fill(INF); 274 | } 275 | let nodesToAnimate = []; 276 | // this will default node for min-heap. 277 | const defaultNode = { 278 | dis: INF, 279 | x: -1, 280 | y: -1, 281 | }; 282 | // heap will be created based on given comparator function. 283 | const DijkstraComparator = (a, b) => { 284 | if (a.dis != b.dis) return a.dis < b.dis; 285 | return Math.abs(a.y - endingPointY) < Math.abs(b.y - endingPointY); 286 | }; 287 | const pq = new PriorityQueue(defaultNode, DijkstraComparator); 288 | 289 | pq.add({ dis: 0, x: startingPointX, y: startingPointY }); 290 | dis[startingPointX][startingPointY] = 0; 291 | vis[startingPointX][startingPointY] = 1; 292 | path[startingPointX][startingPointY] = '1'; 293 | 294 | const dx = [1, 0, -1, 0]; 295 | const dy = [0, 1, 0, -1]; 296 | const direction = ['U', 'L', 'D', 'R']; 297 | 298 | while (!pq.isEmpty()) { 299 | let p = pq.getTop(); 300 | pq.pop(); 301 | let x = p.x; 302 | let y = p.y; 303 | 304 | if (x == endingPointX && y == endingPointY) { 305 | break; 306 | } 307 | if (!(x == startingPointX && y == startingPointY)) { 308 | nodesToAnimate.push({ x: x, y: y }); 309 | } 310 | for (let i = 0; i < 4; i++) { 311 | let newX = x + dx[i]; 312 | let newY = y + dy[i]; 313 | if (isValid(newX, newY) && isBlocked[newX][newY] === 0 && dis[x][y] + 1 < dis[newX][newY]) { 314 | pq.add({ 315 | dis: dis[x][y] + 1, 316 | x: newX, 317 | y: newY, 318 | }); 319 | vis[newX][newY] = 1; 320 | dis[newX][newY] = dis[x][y] + 1; 321 | path[newX][newY] = direction[i]; 322 | } 323 | } 324 | 325 | if (currentlyRunning === false) { 326 | clearGrid(); 327 | return; 328 | } 329 | } 330 | 331 | await animate(nodesToAnimate, speed); 332 | if (vis[endingPointX][endingPointY]) { 333 | foundPath = true; 334 | await animatePath(path); 335 | } else { 336 | alert('There is no path between starting and ending point'); 337 | } 338 | currentlyRunning = false; 339 | }; 340 | 341 | const DFS_BFS = async (type) => { 342 | let vis = new Array(rows); 343 | let path = new Array(rows); 344 | for (let i = 0; i < rows; i++) { 345 | vis[i] = new Array(cols).fill(false); 346 | path[i] = new Array(cols).fill('0'); 347 | } 348 | let nodesToAnimate = []; 349 | 350 | const dq = new Deque(); 351 | dq.push_back([startingPointX, startingPointY]); 352 | vis[startingPointX][startingPointY] = true; 353 | path[startingPointX][startingPointY] = '1'; 354 | 355 | const dx = [1, 0, -1, 0]; 356 | const dy = [0, 1, 0, -1]; 357 | const direction = ['U', 'L', 'D', 'R']; 358 | 359 | while (!dq.isEmpty()) { 360 | let x, y; 361 | if (type == 'dfs') { 362 | [x, y] = dq.pop_back(); 363 | } else { 364 | [x, y] = dq.pop_front(); 365 | } 366 | 367 | if (x == endingPointX && y == endingPointY) { 368 | break; 369 | } 370 | if (!(x == startingPointX && y == startingPointY)) { 371 | nodesToAnimate.push({ x: x, y: y }); 372 | } 373 | for (let i = 0; i < 4; i++) { 374 | let newX = x + dx[i]; 375 | let newY = y + dy[i]; 376 | if (isValid(newX, newY) && isBlocked[newX][newY] == 0 && vis[newX][newY] == false) { 377 | dq.push_back([newX, newY]); 378 | vis[newX][newY] = true; 379 | path[newX][newY] = direction[i]; 380 | } 381 | } 382 | if (currentlyRunning == false) { 383 | clearGrid(); 384 | return; 385 | } 386 | } 387 | 388 | await animate(nodesToAnimate, speed + 4); 389 | if (vis[endingPointX][endingPointY]) { 390 | foundPath = true; 391 | await animatePath(path); 392 | } else { 393 | alert('There is no path between starting and ending point'); 394 | } 395 | currentlyRunning = false; 396 | }; 397 | 398 | const AStar = async () => { 399 | const findManhattanDistance = (sx, sy, fx, fy) => { 400 | return Math.abs(fx - sx) + Math.abs(fy - sy); 401 | }; 402 | const AStarComparator = (x, y) => { 403 | return x.f < y.f; 404 | }; 405 | 406 | const INF = 1000000000; 407 | let vis = new Array(rows); 408 | let path = new Array(rows); 409 | for (let i = 0; i < rows; i++) { 410 | vis[i] = new Array(cols).fill(0); 411 | path[i] = new Array(cols).fill('0'); 412 | } 413 | let nodesToAnimate = []; 414 | 415 | const defaultNode = { 416 | f: INF, 417 | x: -1, 418 | y: -1, 419 | }; 420 | const pq = new PriorityQueue(defaultNode, AStarComparator); 421 | pq.add({ 422 | f: findManhattanDistance(startingPointX, startingPointY, endingPointX, endingPointY), 423 | x: startingPointX, 424 | y: startingPointY, 425 | }); 426 | vis[startingPointX][startingPointY] = 1; 427 | path[startingPointX][startingPointY] = '1'; 428 | 429 | const dx = [1, 0, -1, 0]; 430 | const dy = [0, 1, 0, -1]; 431 | const direction = ['U', 'L', 'D', 'R']; 432 | 433 | while (!pq.isEmpty()) { 434 | let p = pq.getTop(); 435 | pq.pop(); 436 | let x = p.x; 437 | let y = p.y; 438 | 439 | if (x == endingPointX && y == endingPointY) { 440 | break; 441 | } 442 | if (!(x == startingPointX && y == startingPointY)) { 443 | nodesToAnimate.push({ x: x, y: y }); 444 | } 445 | for (let i = 0; i < 4; i++) { 446 | let newX = x + dx[i]; 447 | let newY = y + dy[i]; 448 | if (isValid(newX, newY) && isBlocked[newX][newY] === 0 && vis[newX][newY] === 0) { 449 | let newNode = {}; 450 | pq.add({ 451 | f: findManhattanDistance(newX, newY, endingPointX, endingPointY), 452 | x: newX, 453 | y: newY, 454 | }); 455 | vis[newX][newY] = 1; 456 | path[newX][newY] = direction[i]; 457 | } 458 | } 459 | 460 | if (currentlyRunning === false) { 461 | clearGrid(); 462 | return; 463 | } 464 | } 465 | await animate(nodesToAnimate, speed + 10); 466 | if (vis[endingPointX][endingPointY]) { 467 | foundPath = true; 468 | await animatePath(path); 469 | } else { 470 | alert('There is no path between starting and ending point'); 471 | } 472 | currentlyRunning = false; 473 | }; 474 | 475 | const removeVisitedCell = () => { 476 | // remove "visited" and "path" 477 | for (let i = 0; i < rows; i++) { 478 | for (let j = 0; j < cols; j++) { 479 | if (gridElements[i][j].className == 'visited' || gridElements[i][j].className == 'path') { 480 | gridElements[i][j].className = ''; 481 | } 482 | } 483 | } 484 | }; 485 | 486 | const clearGrid = () => { 487 | // remove "visited", "path" and "block(wall)" cells 488 | currentlyRunning = false; 489 | for (let i = 0; i < rows; i++) { 490 | for (let j = 0; j < cols; j++) { 491 | if ((i == startingPointX && j == startingPointY) || (i == endingPointX && j == endingPointY)); 492 | else { 493 | gridElements[i][j].className = ''; 494 | } 495 | isBlocked[i][j] = 0; 496 | } 497 | } 498 | }; 499 | 500 | const algorithmCaller = () => { 501 | if (currentlyRunning == true) { 502 | return; 503 | } 504 | type = document.getElementById('algorithm_type').value; 505 | currentlyRunning = true; 506 | foundPath = false; 507 | removeVisitedCell(); 508 | if (type == 'dijkstra') { 509 | Dijkstra(); 510 | } else if (type == 'Astar') { 511 | AStar(); 512 | } else if (type == 'bfs' || type == 'dfs') { 513 | DFS_BFS(type); 514 | } 515 | }; 516 | 517 | document.getElementById('visualizeButton').addEventListener('click', algorithmCaller); 518 | document.getElementById('clearButton').addEventListener('click', clearGrid); 519 | -------------------------------------------------------------------------------- /algos/path_finding/styles.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap'); 2 | 3 | 4 | * { 5 | padding: 0; 6 | margin: 0; 7 | box-sizing: border-box; 8 | } 9 | 10 | body { 11 | background: #212833 ; 12 | display: flex; 13 | flex-direction: column; 14 | align-items: center; 15 | font-family: 'Poppins', sans-serif; 16 | box-shadow: inset 0 70px 80px rgb(0 0 0 / 21%); 17 | } 18 | 19 | /* ============= Scrollbar ============= */ 20 | ::-webkit-scrollbar { 21 | width: 4px; 22 | } 23 | ::-webkit-scrollbar-track { 24 | background: #000; 25 | } 26 | ::-webkit-scrollbar-thumb { 27 | background: white; 28 | } 29 | 30 | 31 | 32 | #header { 33 | width: 1400px; 34 | margin-top: 1rem; 35 | align-items: center; 36 | 37 | box-shadow: 10px 10px 30px rgba(0, 0, 0, 0.363); 38 | border-radius: 10px; 39 | background-color: rgba(255, 255, 255, 0.1); 40 | overflow: hidden; 41 | display: flex; 42 | flex-direction: row; 43 | justify-content: center; 44 | border-top: 1px solid rgba(255, 255, 255, 0.4); 45 | border-left: 1px solid rgba(255, 255, 255, 0.4); 46 | font-family: 'Poppins', sans-serif; 47 | backdrop-filter: blur(8px); 48 | } 49 | #header a { 50 | text-decoration: none; 51 | 52 | } 53 | #title { 54 | text-align: center; 55 | font-size: 2rem; 56 | color: rgb(244, 244, 244); 57 | padding: 1rem 0; 58 | font-weight: bold; 59 | } 60 | 61 | 62 | 63 | #algorithm_type { 64 | position: absolute; 65 | right: 0 !important; 66 | top: 1.5rem ; 67 | margin-right: 2rem; 68 | padding: 0.5rem 1rem; 69 | border-radius: 5px; 70 | background: #161824; 71 | color: white; 72 | outline: none; 73 | border: none; 74 | } 75 | #algorithm_type:hover { 76 | cursor: pointer; 77 | } 78 | 79 | #indicator-container { 80 | display: flex; 81 | justify-content: center; 82 | align-items: center; 83 | width: 800px; 84 | background-color: cornflowerblue; 85 | } 86 | #indicator-container p { 87 | font-family: 'Poppins', sans-serif; 88 | font-size: 0.9rem; 89 | font-weight: normal; 90 | 91 | } 92 | 93 | .indicator { 94 | display: flex; 95 | font-weight: bold; 96 | margin: 10px 20px; 97 | } 98 | .indicator-block { 99 | width: 20px; 100 | height: 20px; 101 | margin-right: 5px; 102 | } 103 | #start-indicator { 104 | background-color: rgb(90, 255, 7); 105 | } 106 | #end-indicator { 107 | background-color: red; 108 | } 109 | #visited-indicator { 110 | background-color: orange; 111 | } 112 | #wall-indicator { 113 | background-color: rgb(47, 40, 40); 114 | } 115 | #path-indicator { 116 | background-color: rgb(128, 0, 167); 117 | } 118 | .button { 119 | padding: 0.8rem 1.4rem; 120 | font-size: 1rem !important; 121 | color: white; 122 | border-radius: 5px; 123 | background: #161824; 124 | letter-spacing: 1.2px; 125 | border: none; 126 | outline: none; 127 | transition: all 0.25s ease-in-out; 128 | } 129 | .button:hover { 130 | cursor: pointer; 131 | transform: scale(1.1) perspective(1px); 132 | } 133 | 134 | #visualizeButton { 135 | margin-right: 10px; 136 | } 137 | #clearButton { 138 | margin-left: 10px; 139 | } 140 | 141 | .grid { 142 | margin: 1em auto; 143 | border-collapse: collapse; 144 | } 145 | .grid td { 146 | cursor: pointer; 147 | width: 28px; 148 | height: 28px; 149 | border: 1px solid rgb(114, 161, 236); 150 | text-align: center; 151 | background-color: white; 152 | } 153 | .grid td.clicked { 154 | background-color: rgb(47, 40, 40); 155 | animation: clicked_animation 1.5s 1; 156 | } 157 | 158 | .grid td.startPoint { 159 | background-color: rgb(90, 255, 7); 160 | } 161 | 162 | .grid td.endPoint { 163 | background-color: red; 164 | } 165 | 166 | .grid td.visited { 167 | background-color: orange; 168 | animation: visited_animation 2s 1; 169 | } 170 | 171 | @keyframes clicked_animation { 172 | from { 173 | background-color: rgb(0, 200, 255); 174 | } 175 | to { 176 | /* background-color: yellow; */ 177 | } 178 | } 179 | 180 | @keyframes visited_animation { 181 | from { 182 | border-radius: 5px; 183 | background-color: purple; 184 | } 185 | to { 186 | border-radius: 0px; 187 | background-color: orange; 188 | } 189 | } 190 | 191 | .grid td.path { 192 | background-color: rgb(128, 0, 167); 193 | animation: change_color 1.5s 1; 194 | } 195 | @keyframes change_color { 196 | from { 197 | background-color: rgb(0, 255, 229); 198 | } 199 | to { 200 | background-color: rgb(128, 0, 167); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /algos/sorting/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Algorithm Visualizer | Sorting 10 | 11 | 12 | 13 | 38 |
39 | 40 |
41 | 42 | 43 | 44 |
45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /algos/sorting/script.js: -------------------------------------------------------------------------------- 1 | let heights = []; 2 | let bars = []; 3 | let barValues = []; 4 | 5 | let barSlider = document.getElementById('barSlider'); 6 | let n = barSlider.value; 7 | let speedSlider = document.getElementById('speedSlider'); 8 | let delay = 375 - speedSlider.value; 9 | 10 | let container = document.getElementById('container'); 11 | let width = container.offsetWidth; 12 | let height = container.offsetHeight; 13 | let lineWidth = width / n - 1; 14 | 15 | let isStopped = true; 16 | let isPaused = false; 17 | let isGenerated = true; 18 | let isSorted = false; 19 | 20 | // stack implementation. 21 | class Stack { 22 | constructor() { 23 | this.arr = []; 24 | this.top = -1; 25 | } 26 | push(element) { 27 | this.top++; 28 | this.arr.push(element); 29 | } 30 | isEmpty() { 31 | return this.top == -1; 32 | } 33 | pop() { 34 | if (this.isEmpty() === false) { 35 | this.top = this.top - 1; 36 | return this.arr.pop(); 37 | } 38 | } 39 | } 40 | 41 | // get random value between min and max; 42 | function getRandomValue(min, max) { 43 | return Math.random() * (max - min) + min; 44 | } 45 | // Generate random heights of the bar and create div element of the bar. 46 | function generateRandomArray() { 47 | isGenerated = true; 48 | isSorted = false; 49 | isStopped = true; 50 | isPaused = false; 51 | n = barSlider.value; 52 | lineWidth = width / n - 1; 53 | container.innerHTML = ''; 54 | for (let i = 0; i < n; i++) { 55 | heights[i] = parseInt(getRandomValue(1, height)); 56 | bars.push(document.createElement('div')); 57 | bars[i].style.width = `${lineWidth}px`; 58 | bars[i].style.height = `${heights[i]}px`; 59 | bars[i].style.transform = `translate(${i * lineWidth + i}px)`; 60 | bars[i].style.backgroundColor = 'white'; 61 | bars[i].className = 'bar'; 62 | container.appendChild(bars[i]); 63 | 64 | // if there are more numer of bars then it is not feasible to show bar values because they gets mixed up. 65 | if (n <= 60) { 66 | barValues.push(document.createElement('div')); 67 | barValues[i].innerHTML = heights[i]; 68 | barValues[i].style.marginBottom = `${heights[i] + 5}px`; 69 | barValues[i].style.transform = `translate(${i * lineWidth + i}px)`; 70 | barValues[i].className = 'barValue'; 71 | container.appendChild(barValues[i]); 72 | } 73 | } 74 | } 75 | generateRandomArray(); 76 | 77 | // swap 2 bars and also swap trnasform property for the animation. 78 | function swap(i, minindex) { 79 | [heights[i], heights[minindex]] = [heights[minindex], heights[i]]; 80 | 81 | [bars[i], bars[minindex]] = [bars[minindex], bars[i]]; 82 | [bars[i].style.transform, bars[minindex].style.transform] = [bars[minindex].style.transform, bars[i].style.transform]; 83 | 84 | [barValues[i], barValues[minindex]] = [barValues[minindex], barValues[i]]; 85 | [barValues[i].style.transform, barValues[minindex].style.transform] = [ 86 | barValues[minindex].style.transform, 87 | barValues[i].style.transform, 88 | ]; 89 | } 90 | // Draw bars with their new Updated heights. 91 | function draw(coloredBars, colors) { 92 | // coloredBars contains indices of the bars which will be in different color than default color 93 | // colors array stores color for different bars. 94 | for (let i = 0; i < n; i++) { 95 | bars[i].style.backgroundColor = 'white'; 96 | for (let j = 0; j < coloredBars.length; j++) { 97 | if (i == coloredBars[j]) { 98 | bars[i].style.backgroundColor = colors[j]; 99 | break; 100 | } 101 | } 102 | } 103 | } 104 | 105 | // to put delay between visualization. 106 | function sleep(ms) { 107 | return new Promise((resolve) => setTimeout(resolve, ms)); 108 | } 109 | // Play animation after sorting process is finished 110 | async function SortedAnimation() { 111 | // first we will go from left to right and color them in some color. 112 | // then we will again go from left to right and color them white. 113 | for (let i = 0; i < n; i++) { 114 | bars[i].style.backgroundColor = 'lime'; 115 | await sleep(10); 116 | } 117 | await sleep(300); 118 | for (let i = 0; i < n; i++) { 119 | bars[i].style.backgroundColor = 'white'; 120 | await sleep(10); 121 | } 122 | } 123 | 124 | // Sorting algos implementation starts... 125 | async function bubbleSort() { 126 | for (let i = 0; i < n - 1; i++) { 127 | for (let j = 0; j < n - i - 1; j++) { 128 | if (isStopped) { 129 | draw([], []); 130 | return; 131 | } 132 | if (!isPaused) { 133 | if (heights[j] > heights[j + 1]) { 134 | swap(j, j + 1); 135 | } 136 | draw([j, j + 1], ['green', 'yellow']); 137 | } else { 138 | j--; 139 | } 140 | await sleep(delay); 141 | } 142 | } 143 | console.log('Bubble sort completed.'); 144 | draw([], []); 145 | isSorted = true; 146 | isStopped = true; 147 | isPaused = false; 148 | SortedAnimation(); 149 | } 150 | 151 | async function selectionSort() { 152 | for (let i = 0; i < n - 1; i++) { 153 | let minIndex = i; 154 | for (let j = i + 1; j < n; j++) { 155 | if (isStopped) { 156 | draw([], []); 157 | return; 158 | } 159 | if (!isPaused) { 160 | if (heights[j] < heights[minIndex]) { 161 | minIndex = j; 162 | } 163 | draw([i, j, minIndex], ['blue', 'red', 'green']); 164 | } else { 165 | j--; 166 | } 167 | await sleep(delay); 168 | } 169 | swap(i, minIndex); 170 | } 171 | console.log('Selection sort completed.'); 172 | draw([], []); 173 | isSorted = true; 174 | isStopped = true; 175 | isPaused = false; 176 | SortedAnimation(); 177 | } 178 | 179 | async function insertionSort() { 180 | for (let i = 0; i < n; i++) { 181 | let key = heights[i]; 182 | for (let j = i - 1; j >= 0 && heights[j] > key; j--) { 183 | if (isStopped) { 184 | draw([], []); 185 | return; 186 | } 187 | if (!isPaused) { 188 | swap(j, j + 1); 189 | draw([j, i + 1], ['green', 'red']); 190 | } else { 191 | j++; 192 | } 193 | await sleep(delay); 194 | } 195 | } 196 | console.log('Insertion sort completed.'); 197 | draw([], []); 198 | isSorted = true; 199 | isStopped = true; 200 | isPaused = false; 201 | SortedAnimation(); 202 | } 203 | 204 | async function mergeSort() { 205 | for (let curSize = 1; curSize < n; curSize *= 2) { 206 | for (let start = 0; start < n - 1; start += 2 * curSize) { 207 | let mid = Math.min(start + curSize - 1, n - 1); 208 | let end = Math.min(start + 2 * curSize - 1, n - 1); 209 | let n1 = mid - start + 1; 210 | let n2 = end - mid; 211 | let L = [], 212 | R = []; 213 | for (let i = 0; i < n1; i++) L.push(heights[start + i]); 214 | for (let j = 0; j < n2; j++) R.push(heights[mid + 1 + j]); 215 | let i = 0, 216 | j = 0, 217 | k = start; 218 | 219 | let barsIndices = []; 220 | let barsColors = []; 221 | for (let i1 = start; i1 <= end; i1++) { 222 | barsIndices.push(i1); 223 | barsColors.push('yellow'); 224 | } 225 | 226 | while (i < n1 || j < n2) { 227 | if (isStopped) { 228 | draw([], []); 229 | return; 230 | } 231 | if (!isPaused) { 232 | if (j == n2 || (i < n1 && L[i] <= R[j])) { 233 | draw([k, ...barsIndices], ['green', ...barsColors]); 234 | i++; 235 | } else { 236 | for (let i1 = mid + 1 + j; i1 > k; i1--) { 237 | swap(i1, i1 - 1); 238 | } 239 | draw([k, ...barsIndices], ['green', ...barsColors]); 240 | j++; 241 | } 242 | k++; 243 | } 244 | await sleep(delay); 245 | } 246 | } 247 | } 248 | console.log('Merge sort completed.'); 249 | draw([], []); 250 | isSorted = true; 251 | isStopped = true; 252 | isPaused = false; 253 | SortedAnimation(); 254 | } 255 | 256 | async function quickSort() { 257 | let s = new Stack(); 258 | s.push(0); 259 | s.push(n - 1); 260 | while (!s.isEmpty()) { 261 | let h = s.pop(); 262 | let l = s.pop(); 263 | 264 | let i = l - 1; 265 | 266 | let barsIndices = []; 267 | let barsColors = []; 268 | for (let i1 = l; i1 <= h; i1++) { 269 | barsIndices.push(i1); 270 | barsColors.push('yellow'); 271 | } 272 | 273 | for (let j = l; j <= h - 1; j++) { 274 | if (isStopped) { 275 | draw([], []); 276 | return; 277 | } 278 | if (!isPaused) { 279 | draw([i, j, ...barsIndices], ['green', 'red', ...barsColors]); 280 | if (heights[j] <= heights[h]) { 281 | i++; 282 | swap(i, j); 283 | } 284 | } else { 285 | j--; 286 | } 287 | await sleep(delay); 288 | } 289 | swap(i + 1, h); 290 | let partition = i + 1; 291 | if (l < partition - 1) { 292 | s.push(l); 293 | s.push(partition - 1); 294 | } 295 | if (partition + 1 < h) { 296 | s.push(partition + 1); 297 | s.push(h); 298 | } 299 | } 300 | console.log('Quick sort completed.'); 301 | draw([], []); 302 | isSorted = true; 303 | isStopped = true; 304 | isPaused = false; 305 | SortedAnimation(); 306 | } 307 | 308 | // when slider value is changed generate new bars and update the value of bar count on the navbar. 309 | barSlider.oninput = () => { 310 | document.querySelector('.sliderValue').innerHTML = `Bars: ${barSlider.value}`; 311 | generateRandomArray(); 312 | }; 313 | speedSlider.oninput = () => { 314 | delay = 375 - speedSlider.value; 315 | }; 316 | 317 | document.getElementById('generateButton').addEventListener('click', generateRandomArray); 318 | document.getElementById('sortButton').addEventListener('click', () => { 319 | // get the name of selected sorting algorithm. 320 | type = document.getElementById('sort_type').value; 321 | 322 | // if there is another sorting visualization going on then return from the function. 323 | if (!isStopped) return; 324 | // if recently we used visualization and bars are sorted then generate new unsorted array. 325 | if (isSorted || !isGenerated) generateRandomArray(); 326 | 327 | isGenerated = false; 328 | isPaused = false; 329 | isStopped = false; 330 | 331 | if (type == 'bubble') bubbleSort(); 332 | else if (type == 'selection') selectionSort(); 333 | else if (type == 'insertion') insertionSort(); 334 | else if (type == 'merge') mergeSort(); 335 | else if (type == 'quick') quickSort(); 336 | }); 337 | document.getElementById('stopButton').addEventListener('click', () => { 338 | isStopped = true; 339 | isPaused = false; 340 | document.getElementById('pauseButton').innerHTML = 'Pause'; 341 | // if user presses stop button and random bars is not generated then generate rnadom bars. 342 | if (!isGenerated && !isSorted) generateRandomArray(); 343 | }); 344 | 345 | document.getElementById('pauseButton').addEventListener('click', () => { 346 | // if currently sorting is in progress then isStopped will be false. 347 | if (!isStopped) { 348 | // toggle button between pause and resume 349 | if (isPaused) { 350 | document.getElementById('pauseButton').innerHTML = 'Pause'; 351 | isPaused = false; 352 | } else { 353 | document.getElementById('pauseButton').innerHTML = 'Resume'; 354 | isPaused = true; 355 | } 356 | } 357 | }); 358 | -------------------------------------------------------------------------------- /algos/sorting/styles.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap'); 2 | 3 | 4 | * { 5 | padding: 0; 6 | margin: 0; 7 | box-sizing: border-box; 8 | } 9 | 10 | body { 11 | background: #212833 ; 12 | box-shadow: inset 0 70px 80px rgb(0 0 0 / 21%); 13 | display: flex; 14 | flex-direction: column; 15 | align-items: center; 16 | justify-content: center; 17 | margin: auto; 18 | font-family: 'Poppins', sans-serif; 19 | 20 | } 21 | 22 | /* ============= Scrollbar ============= */ 23 | ::-webkit-scrollbar { 24 | width: 4px; 25 | } 26 | ::-webkit-scrollbar-track { 27 | background: #000; 28 | } 29 | ::-webkit-scrollbar-thumb { 30 | background: white; 31 | } 32 | 33 | 34 | 35 | #header { 36 | width: 1400px; 37 | margin-top: 1rem; 38 | 39 | box-shadow: 10px 10px 30px rgba(0, 0, 0, 0.363); 40 | border-radius: 10px; 41 | background-color: rgba(255, 255, 255, 0.1); 42 | overflow: hidden; 43 | display: flex; 44 | align-items: center; 45 | justify-content: space-between; 46 | border-top: 1px solid rgba(255, 255, 255, 0.4); 47 | border-left: 1px solid rgba(255, 255, 255, 0.4); 48 | font-family: 'Poppins', sans-serif; 49 | backdrop-filter: blur(8px); 50 | } 51 | #header a { 52 | text-decoration: none; 53 | 54 | } 55 | #title { 56 | text-align: center; 57 | font-size: 2rem; 58 | /* color: rgb(90, 0, 90) ; */ 59 | color: rgb(244, 244, 244); 60 | padding: 1rem 0; 61 | font-weight: bold; 62 | } 63 | 64 | 65 | 66 | #header > div { 67 | flex-grow: 1; 68 | } 69 | 70 | #headerLeft { 71 | display: flex; 72 | align-items: center; 73 | padding-left: 40px; 74 | justify-content: space-between; 75 | width: 65%; 76 | } 77 | .headerLeftContent { 78 | display: flex; 79 | } 80 | .slidecontainer { 81 | position: relative; 82 | margin-right: 20px; 83 | } 84 | .slider { 85 | outline: none; 86 | opacity: 0.7; 87 | -webkit-transition: 0.2s; 88 | transition: opacity 0.2s; 89 | } 90 | .slider:hover { 91 | opacity: 1; 92 | } 93 | .slider::-webkit-slider-thumb { 94 | cursor: pointer; 95 | } 96 | .sliderValue { 97 | position: absolute; 98 | top: 20px; 99 | left: 40px; 100 | } 101 | .speedValue { 102 | position: absolute; 103 | top: 20px; 104 | left: 40px; 105 | } 106 | 107 | a { 108 | text-decoration: none; 109 | } 110 | 111 | #headerRight { 112 | text-align: right; 113 | padding-right: 55px; 114 | width: 35%; 115 | } 116 | #sort_type { 117 | position: absolute; 118 | right: 0 !important; 119 | top: 1.5rem ; 120 | margin-right: 2rem; 121 | padding: 0.5rem 1rem; 122 | border-radius: 5px; 123 | background: #161824; 124 | color: white; 125 | outline: none; 126 | border: none; 127 | } 128 | 129 | #container { 130 | display: flex; 131 | width: 99%; 132 | min-width: 99%; 133 | height: 570px; 134 | margin-left: auto; 135 | margin-right: auto; 136 | position: relative; 137 | margin-top: 25px; 138 | } 139 | 140 | .bar { 141 | margin-right: 1px; 142 | align-self: flex-end; 143 | transition: 0.2s transform ease; 144 | position: absolute; 145 | } 146 | .barValue { 147 | margin-right: 1px; 148 | align-self: flex-end; 149 | transition: 0.2s transform ease; 150 | position: absolute; 151 | color: red; 152 | font-weight: bold; 153 | } 154 | 155 | .button { 156 | padding: 0.8rem 1.4rem; 157 | font-size: 1rem; 158 | color: white; 159 | border-radius: 5px; 160 | background: #161824; 161 | letter-spacing: 1.2px; 162 | border: none; 163 | outline: none; 164 | margin-top: 1rem; 165 | transition: all 0.25s ease-in-out; 166 | } 167 | .button:hover { 168 | cursor: pointer; 169 | transform: scale(1.1) perspective(1px); 170 | } 171 | #buttonsdiv { 172 | text-align: center; 173 | margin-bottom: 30px; 174 | } 175 | #sortButton { 176 | margin-right: 10px; 177 | } 178 | #stopButton { 179 | margin-left: 10px; 180 | } 181 | 182 | #generateButton { 183 | padding: 0.4rem 0.8rem; 184 | font-size: 16px; 185 | border-radius: 5px; 186 | margin-top: 0px; 187 | font-size: 0.9rem; 188 | } 189 | #generateButton:hover { 190 | transform: scale(1.08) perspective(1px); 191 | } 192 | 193 | @media (max-width: 900px) { 194 | #title { 195 | font-size: 0px; 196 | } 197 | .slidecontainer, 198 | #sort_type { 199 | margin-top: 0px; 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /algos/trie/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Algorithm Visualizer | Trie 11 | 12 | 13 |
14 | 23 |
24 |
25 |
26 |

Edge

27 |
28 |
29 |
30 |

Node

31 |
32 |
33 |
34 |

End of Word Node

35 |
36 | 37 |
38 |
39 |
40 | 41 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /algos/trie/script.js: -------------------------------------------------------------------------------- 1 | // prerequisite: javascript, d3.js 2 | 3 | // structure of node 4 | // { 5 | // nodeId: number <--- unique id for each node. it will be also used as id for html element of node. 6 | // value: character <-- this will store character stored in current node 7 | // endOfWord: boolean <-- show node as the end of word. 8 | // children: [] <--- this array will store all the children nodes and its values 9 | // childrenCharacter : {} <-- this will store all children character. 10 | // } 11 | 12 | // Tree will be stored as object. 13 | let data = { nodeId: 0, value: 'root', endOfWord: false, children: [], childrenCharacter: {} }; 14 | // Current available id for the node. We will give each node a unique id and put this as their html element "id". 15 | let curId = 1; 16 | 17 | const width = Math.max(100, window.innerWidth - 50); 18 | const height = Math.max(100, window.innerHeight - 200); 19 | const nodeRadius = 20; 20 | const LinkStroke = 4; 21 | const animationDuration = 500; 22 | const padding = 40; 23 | 24 | d3.select('.Canvas').append('svg').append('g'); 25 | 26 | // During insertion or deletion visualization process disbale the buttons 27 | const freezeButtons = () => { 28 | document.getElementById('InsertButton').disabled = true; 29 | document.getElementById('DeleteButton').disabled = true; 30 | }; 31 | const unfreezeButtons = () => { 32 | document.getElementById('InsertButton').disabled = false; 33 | document.getElementById('DeleteButton').disabled = false; 34 | }; 35 | 36 | // To put delay between visualization. 37 | const sleep = (ms) => { 38 | return new Promise((resolve) => setTimeout(resolve, ms)); 39 | }; 40 | 41 | // Create the root node. 42 | const init = () => { 43 | const treemap = d3.tree().size([width, height]); 44 | const newTree = treemap(d3.hierarchy(data, (d) => d.children)); 45 | 46 | newTree.y += padding; 47 | 48 | const nodes = d3 49 | .select('.Canvas > svg g') 50 | .selectAll('g.node') 51 | .data(newTree) 52 | .enter() 53 | .append('g') 54 | .attr('class', () => 'node'); 55 | nodes 56 | .append('circle') 57 | .attr('id', (d) => `circle${d.data.nodeId}`) 58 | .attr('r', nodeRadius) 59 | .attr('cx', (d) => d.x) 60 | .attr('cy', (d) => d.y) 61 | .attr('value', (d) => d.data.value); 62 | nodes 63 | .append('text') 64 | .attr('dx', (d) => d.x) 65 | .attr('dy', (d) => d.y) 66 | .attr('text-anchor', 'middle') 67 | .attr('alignment-baseline', 'middle') 68 | .attr('font-size', '20px') 69 | .attr('font-weight', 'bold') 70 | .text((d) => d.data.value); 71 | }; 72 | init(); 73 | 74 | // To animate from old tree to new tree. 75 | const update = (oldData, newData, parentId, childId) => { 76 | // childId is node we want to insert/delete and parentId is parent of node we want to insert/delete. 77 | 78 | /* 79 | Small description of "update" function. 80 | -> Find the co-ordinates of old tree. 81 | -> Find the co-ordinates of new updated tree. 82 | -> Put tree on old co-ordinates. 83 | -> Animate nodes and links to the new co-ordinates. 84 | */ 85 | 86 | // Create the old and new updated tree. 87 | const treemap = d3.tree().size([width, height]); 88 | const oldTree = treemap(d3.hierarchy(oldData, (d) => d.children)); 89 | const newTree = treemap(d3.hierarchy(newData, (d) => d.children)); 90 | // Convert both trees from javascript objects to array. 91 | let oldTreeArray = oldTree.descendants(); 92 | let newTreeArray = newTree.descendants(); 93 | 94 | // Putting old and new co-ordinates of nodes in the same array. 95 | for (let i = 0; i < newTreeArray.length; i++) { 96 | let oldPosition = {}; 97 | // Traverse the old tree and find the old co-ordinates. 98 | for (let j = 0; j < oldTreeArray.length; j++) { 99 | if (newTreeArray[i].data.nodeId == childId) { 100 | // Node which we are going to insert, there is no old co-oridnates available 101 | // So we are going to use the co-ordinates of parent node. 102 | if (oldTreeArray[j].data.nodeId == parentId) { 103 | oldPosition = oldTreeArray[j]; 104 | } 105 | } else { 106 | if (oldTreeArray[j].data.nodeId == newTreeArray[i].data.nodeId) { 107 | oldPosition = oldTreeArray[j]; 108 | } 109 | } 110 | } 111 | newTreeArray[i].oldX = oldPosition.x || 0; 112 | newTreeArray[i].oldY = (oldPosition.y || 0) + padding; 113 | newTreeArray[i].y += padding; 114 | } 115 | 116 | // Remove the old tree from canvas. we will draw new one. 117 | d3.select('.Canvas > svg g').remove(); 118 | d3.select('.Canvas > svg').append('g'); 119 | 120 | // Create all the edges and put them in new array. 121 | let allLinks = []; 122 | for (let i = 0; i < newTreeArray.length; i++) { 123 | if (!newTreeArray[i].children) continue; 124 | for (let j = 0; j < newTreeArray[i].children.length; j++) { 125 | allLinks.push({ 126 | parent: newTreeArray[i], 127 | child: newTreeArray[i].children[j], 128 | }); 129 | } 130 | } 131 | 132 | // Drawing edges on canvas with some styles and co-ordinates. 133 | const links = d3 134 | .select('.Canvas > svg g') 135 | .selectAll('g.link') 136 | .data(allLinks) 137 | .enter() 138 | .append('g') 139 | .append('line') 140 | .attr('id', (d) => `link${d.parent.data.nodeId}D${d.child.nodeId}`) 141 | .attr('stroke-width', LinkStroke) 142 | .attr('stroke', 'black') 143 | .attr('x1', (d) => d.parent.oldX) 144 | .attr('y1', (d) => d.parent.oldY) 145 | .attr('x2', (d) => d.child.oldX) 146 | .attr('y2', (d) => d.child.oldY); 147 | // Transition from old tree to new tree. move old edges to new edges using co-ordinates. 148 | links 149 | .transition() 150 | .duration(animationDuration) 151 | .attr('x1', (d) => d.parent.x) 152 | .attr('y1', (d) => d.parent.y) 153 | .attr('x2', (d) => d.child.x) 154 | .attr('y2', (d) => d.child.y); 155 | 156 | // Draw nodes and their value on screen using old tree co-ordinates. 157 | const nodes = d3 158 | .select('.Canvas > svg g') 159 | .selectAll('g.node') 160 | .data(newTree) 161 | .enter() 162 | .append('g') 163 | .attr('id', (d) => `node${d.data.nodeId}`) 164 | .attr('class', (d) => (d.data.endOfWord ? 'endOfWordNode' : 'node')); 165 | nodes 166 | .append('circle') 167 | .attr('id', (d) => `circle${d.data.nodeId}`) 168 | .attr('r', nodeRadius) 169 | .attr('cx', (d) => d.oldX) 170 | .attr('cy', (d) => d.oldY) 171 | .attr('value', (d) => d.data.value); 172 | nodes 173 | .append('text') 174 | .attr('dx', (d) => d.oldX) 175 | .attr('dy', (d) => d.oldY) 176 | .attr('text-anchor', 'middle') 177 | .attr('alignment-baseline', 'middle') 178 | .attr('fill', 'white') 179 | .attr('font-size', '20px') 180 | .attr('font-weight', 'bold') 181 | .text((d) => d.data.value); 182 | 183 | // Move nodes from old co-ordinate to new co-ordinates. 184 | nodes 185 | .transition() 186 | .duration(animationDuration) 187 | .attr('transform', function (d) { 188 | if (d.data.value != null) return `translate(${parseInt(d.x - d.oldX)}, ${parseInt(d.y - d.oldY)})`; 189 | else return 'translate(0,0)'; 190 | }); 191 | 192 | data = newData; 193 | }; 194 | 195 | const addNode = async () => { 196 | // Get the node value from input field and verify it's value/type. 197 | let str = document.getElementById('InsertNodeField').value; 198 | if (str == '') { 199 | return; 200 | } 201 | if (str.length > 12) { 202 | alert('Word Length should be less than 12.'); 203 | return; 204 | } 205 | str = str.toLowerCase(); 206 | document.getElementById('InsertNodeField').value = ''; 207 | 208 | // Freeze(disable) insert and delete buttons. 209 | freezeButtons(); 210 | 211 | // Copying object without reference in a dirty way. Might make proper function to copy object later. 212 | let oldData = JSON.parse(JSON.stringify(data)); 213 | let newData = JSON.parse(JSON.stringify(data)); 214 | let node = newData; 215 | 216 | // Logic of trie insertion with some animation. 217 | for (let i = 0; i < str.length; i++) { 218 | if (str[i] in node.childrenCharacter && node.childrenCharacter[str[i]] == true) { 219 | // Character Node already exits. Just find it and go there. 220 | for (let j = 0; j < node.children.length; j++) { 221 | if (node.children[j].value == str[i]) { 222 | node = node.children[j]; 223 | break; 224 | } 225 | } 226 | if (i == str.length - 1) { 227 | node.endOfWord = true; 228 | update(newData, newData, -1, -1); 229 | } 230 | } else { 231 | // Create a node for character. Show node creation animation. 232 | node.childrenCharacter[str[i]] = true; 233 | node.children.push({ 234 | nodeId: curId, 235 | value: str[i], 236 | endOfWord: i == str.length - 1, 237 | children: [], 238 | childrenCharacter: {}, 239 | }); 240 | curId++; 241 | update(oldData, newData, node.nodeId, node.children[node.children.length - 1].nodeId); 242 | oldData = JSON.parse(JSON.stringify(newData)); 243 | node = node.children[node.children.length - 1]; 244 | } 245 | // Show current node in different color(here red). 246 | const nodeEle = document.getElementById(`node${node.nodeId}`); 247 | let originalClass = ''; 248 | if (nodeEle) { 249 | originalClass = nodeEle.className.baseVal; 250 | nodeEle.className.baseVal = 'highlightedNode'; 251 | } 252 | await sleep(700); 253 | // Revert back node to it's original status. 254 | if (nodeEle) { 255 | nodeEle.className.baseVal = originalClass; 256 | } 257 | } 258 | unfreezeButtons(); 259 | }; 260 | 261 | const deleteNodeUtil = async () => { 262 | // If data object has no children then return. 263 | if (data.children.length == 0) { 264 | alert('Trie is empty'); 265 | return; 266 | } 267 | // Get the word we want to delete from input field. 268 | let str = document.getElementById('DeleteNodeField').value; 269 | if (str == '') { 270 | return; 271 | } 272 | str = str.toLowerCase(); 273 | document.getElementById('DeleteNodeField').value = ''; 274 | 275 | // Freeze(disable) insert and delete buttons. 276 | freezeButtons(); 277 | 278 | // Copying object without reference in a dirty way. Might make proper function to copy object later. 279 | const newData = JSON.parse(JSON.stringify(data)); 280 | 281 | // Creating another function for deletion because we are going to write recursive logic to delete word. 282 | const deleteNode = async (parent, node, str, depth) => { 283 | // Highlight the node which we are currently processing. 284 | const nodeEle = document.getElementById(`node${node.nodeId}`); 285 | let originalClass = ''; 286 | if (nodeEle) { 287 | originalClass = nodeEle.className.baseVal; 288 | nodeEle.className.baseVal = 'highlightedNode'; 289 | } 290 | await sleep(700); 291 | if (nodeEle) { 292 | nodeEle.className.baseVal = originalClass; 293 | } 294 | 295 | if (depth == str.length) { 296 | // If last character of word is being processed 297 | if (node.endOfWord == false) { 298 | alert('Word not found in trie'); 299 | return false; 300 | } 301 | // This node is no more end of word after removal of given word. 302 | node.endOfWord = false; 303 | 304 | // If there are still some children to current node 305 | // Then it means there some words which are ending below. 306 | // So we cant delete it. 307 | // Check if current node has some children or not. 308 | if (node.children.length == 0) { 309 | // Copying object without reference in a dirty way. Might make proper function to copy object later. 310 | const oldData = JSON.parse(JSON.stringify(newData)); 311 | // Delete the current node from parent node's "children" and "childrenCharacter" property. 312 | delete parent.childrenCharacter[str[depth - 1]]; 313 | let charIndex = 0; 314 | for (let i = 0; i < parent.children.length; i++) { 315 | if (parent.children[i].value == str[depth - 1]) { 316 | charIndex = i; 317 | } 318 | } 319 | parent.children.splice(charIndex, 1); 320 | update(oldData, newData, -1, -1); 321 | } else { 322 | update(newData, newData, -1, -1); 323 | } 324 | return true; 325 | } 326 | 327 | // If not last character, call recursively for the next character. 328 | // obtained using ASCII value 329 | if (str[depth] in node.childrenCharacter) { 330 | let charIndex = 0; 331 | for (let i = 0; i < node.children.length; i++) { 332 | if (node.children[i].value == str[depth]) { 333 | charIndex = i; 334 | } 335 | } 336 | 337 | const isWordFound = await deleteNode(node, node.children[charIndex], str, depth + 1); 338 | if (isWordFound == false) { 339 | return false; 340 | } 341 | 342 | // Currently we are returning from recursive calls and going back. 343 | 344 | // If current node does not have any child (ts only child got 345 | // deleted), and it is not end of another word. 346 | if (parent && node.children.length == 0 && node.endOfWord == false) { 347 | const nodeEle = document.getElementById(`node${node.nodeId}`); 348 | if (nodeEle) { 349 | nodeEle.className.baseVal = 'highlightedNode'; 350 | } 351 | await sleep(700); 352 | 353 | const oldData = JSON.parse(JSON.stringify(newData)); 354 | // Delete the current node from parent node's "children" and "childrenCharacter" property. 355 | parent.childrenCharacter[str[depth - 1]] = false; 356 | charIndex = 0; 357 | for (let i = 0; i < parent.children.length; i++) { 358 | if (parent.children[i].value == str[depth - 1]) { 359 | charIndex = i; 360 | } 361 | } 362 | parent.children.splice(charIndex, 1); 363 | update(oldData, newData, -1, -1); 364 | } 365 | return true; 366 | } else { 367 | alert('Word not found in trie'); 368 | return false; 369 | } 370 | }; 371 | 372 | let node = newData; 373 | let parent = null; 374 | await deleteNode(parent, node, str, 0); 375 | 376 | unfreezeButtons(); 377 | }; 378 | 379 | document.getElementById('InsertButton').addEventListener('click', addNode); 380 | document.getElementById('DeleteButton').addEventListener('click', deleteNodeUtil); 381 | 382 | // If during writing in insertion or deletion input field, user presses enter key then click on insertion/deletion button. 383 | document.getElementById('InsertNodeField').addEventListener('keyup', function (event) { 384 | if (event.key === 'Enter') { 385 | document.getElementById('InsertButton').click(); 386 | } 387 | }); 388 | document.getElementById('DeleteNodeField').addEventListener('keyup', function (event) { 389 | if (event.key === 'Enter') { 390 | document.getElementById('DeleteButton').click(); 391 | } 392 | }); 393 | -------------------------------------------------------------------------------- /algos/trie/styles.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap'); 2 | 3 | 4 | * { 5 | padding: 0; 6 | margin: 0; 7 | box-sizing: border-box; 8 | } 9 | 10 | body { 11 | background: #212833 ; 12 | box-shadow: inset 0 70px 80px rgb(0 0 0 / 21%); 13 | font-family: 'Poppins', sans-serif; 14 | } 15 | 16 | /* ============= Scrollbar ============= */ 17 | ::-webkit-scrollbar { 18 | width: 4px; 19 | } 20 | ::-webkit-scrollbar-track { 21 | background: #000; 22 | } 23 | ::-webkit-scrollbar-thumb { 24 | background: white; 25 | } 26 | 27 | 28 | .container { 29 | display: flex; 30 | flex-direction: column; 31 | justify-content: center; 32 | align-items: center; 33 | display: flex; 34 | 35 | } 36 | #header { 37 | width: 1400px; 38 | margin-top: 1rem; 39 | 40 | box-shadow: 10px 10px 30px rgba(0, 0, 0, 0.363); 41 | border-radius: 10px; 42 | background-color: rgba(255, 255, 255, 0.1); 43 | overflow: hidden; 44 | display: flex; 45 | flex-direction: row; 46 | justify-content: center; 47 | border-top: 1px solid rgba(255, 255, 255, 0.4); 48 | border-left: 1px solid rgba(255, 255, 255, 0.4); 49 | font-family: 'Poppins', sans-serif; 50 | backdrop-filter: blur(8px); 51 | } 52 | #header a { 53 | text-decoration: none; 54 | 55 | } 56 | #title { 57 | text-align: center; 58 | font-size: 2rem; 59 | /* color: rgb(90, 0, 90) ; */ 60 | color: rgb(244, 244, 244); 61 | padding: 1rem 0; 62 | font-weight: bold; 63 | } 64 | 65 | .container { 66 | display: flex; 67 | flex-flow: column; 68 | height: 100%; 69 | } 70 | #OperationContainer { 71 | position: absolute; 72 | top: 25px; 73 | left: 20px; 74 | display: flex; 75 | align-items: center; 76 | } 77 | #OperationContainer button { 78 | background: #161824; 79 | margin-left: 0.5rem; 80 | outline: none; 81 | border: none; 82 | padding: 0.4rem 1rem; 83 | color: #fff; 84 | border-radius: 2px; 85 | } 86 | #OperationContainer button:hover { 87 | cursor: pointer; 88 | } 89 | #OperationContainer input { 90 | padding-left: 0.4rem !important; 91 | padding: 0.3rem 0; 92 | border-radius: 2px; 93 | outline: none; 94 | border: 1px solid transparent; 95 | } 96 | #OperationContainer input:focus { 97 | border: 1px solid rgb(183, 0, 255); 98 | } 99 | 100 | 101 | #InsertNodeField, 102 | #DeleteNodeField { 103 | width: 110px; 104 | } 105 | #DeleteNodeField { 106 | margin-left: 10px; 107 | } 108 | 109 | .Canvas { 110 | width: 100%; 111 | height: 100vh; 112 | flex: 1 1 auto; 113 | /* background-color: #212833; */ 114 | overflow: hidden; 115 | margin-left: auto; 116 | margin-right: auto; 117 | position: relative; 118 | } 119 | 120 | 121 | 122 | 123 | 124 | .indicator_container { 125 | display: flex; 126 | align-items: center; 127 | justify-content: center; 128 | background-color: cornflowerblue; 129 | padding: 0.5rem 1rem ; 130 | font-size: 0.9rem; 131 | gap: 2rem; 132 | width: 800px; 133 | } 134 | 135 | .indicator { 136 | display: flex; 137 | align-items: center; 138 | } 139 | .indicator_block { 140 | margin-right: 0.3rem; 141 | } 142 | .indicator #edge{ 143 | height: 0.2rem; 144 | width: 2.5rem; 145 | background-color: #099691; 146 | box-shadow: 0 0 1px #333; 147 | } 148 | 149 | .indicator #node, .indicator #end_node{ 150 | height: 1.5rem; 151 | width: 1.5rem; 152 | border-radius: 50%; 153 | border: 1px solid red; 154 | } 155 | .indicator #node { 156 | background-color: #fff; 157 | 158 | } 159 | .indicator #end_node { 160 | background-color: #00911d; 161 | 162 | } 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | .indicator text { 173 | font-size: 20px !important; 174 | } 175 | 176 | svg { 177 | margin-top: 5px; 178 | width: 100%; 179 | /* max-width: 1200px; */ 180 | height: 100%; 181 | overflow: scroll; 182 | } 183 | 184 | .node circle { 185 | fill: #ffffff; 186 | stroke: #ff7d12; 187 | stroke-width: 1px; 188 | } 189 | .node text { 190 | font-size: 22px; 191 | fill: rgb(250, 7, 7); 192 | } 193 | 194 | .endOfWordNode circle { 195 | fill: #00911d; 196 | stroke: #ffffff; 197 | stroke-width: 1px; 198 | } 199 | .endOfWordNode text { 200 | font-size: 23px; 201 | fill: #ffffff; 202 | } 203 | 204 | .highlightedNode circle { 205 | fill: red; 206 | stroke: red; 207 | stroke-width: 2px; 208 | } 209 | .highlightedNode text { 210 | font-size: 23px; 211 | fill: white; 212 | } 213 | 214 | line { 215 | stroke: #099b96; 216 | } 217 | 218 | .null-node, 219 | .null-link { 220 | visibility: hidden; 221 | } 222 | text { 223 | fill: #ffffff; 224 | } 225 | -------------------------------------------------------------------------------- /assets/banner.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayan-Maity/Algorithm-Visualizer/7cf61e878afc42c273579d2b8f2a0a192c480b5f/assets/banner.webp -------------------------------------------------------------------------------- /assets/bst.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayan-Maity/Algorithm-Visualizer/7cf61e878afc42c273579d2b8f2a0a192c480b5f/assets/bst.jpg -------------------------------------------------------------------------------- /assets/grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayan-Maity/Algorithm-Visualizer/7cf61e878afc42c273579d2b8f2a0a192c480b5f/assets/grid.png -------------------------------------------------------------------------------- /assets/home-page.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayan-Maity/Algorithm-Visualizer/7cf61e878afc42c273579d2b8f2a0a192c480b5f/assets/home-page.jpeg -------------------------------------------------------------------------------- /assets/path-preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayan-Maity/Algorithm-Visualizer/7cf61e878afc42c273579d2b8f2a0a192c480b5f/assets/path-preview.jpg -------------------------------------------------------------------------------- /assets/path.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayan-Maity/Algorithm-Visualizer/7cf61e878afc42c273579d2b8f2a0a192c480b5f/assets/path.jpg -------------------------------------------------------------------------------- /assets/preview2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayan-Maity/Algorithm-Visualizer/7cf61e878afc42c273579d2b8f2a0a192c480b5f/assets/preview2.jpg -------------------------------------------------------------------------------- /assets/sorting-preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayan-Maity/Algorithm-Visualizer/7cf61e878afc42c273579d2b8f2a0a192c480b5f/assets/sorting-preview.jpg -------------------------------------------------------------------------------- /assets/sorting.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayan-Maity/Algorithm-Visualizer/7cf61e878afc42c273579d2b8f2a0a192c480b5f/assets/sorting.jpg -------------------------------------------------------------------------------- /assets/trie-preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayan-Maity/Algorithm-Visualizer/7cf61e878afc42c273579d2b8f2a0a192c480b5f/assets/trie-preview.jpg -------------------------------------------------------------------------------- /assets/trie.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayan-Maity/Algorithm-Visualizer/7cf61e878afc42c273579d2b8f2a0a192c480b5f/assets/trie.jpg -------------------------------------------------------------------------------- /css/styles.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap'); 2 | 3 | 4 | * { 5 | padding: 0; 6 | margin: 0; 7 | box-sizing: border-box; 8 | } 9 | 10 | body { 11 | background: url("../assets/grid.png") #2b3443 ; 12 | background-position: center; 13 | background-repeat: no-repeat; 14 | box-shadow: inset 0 70px 80px rgb(0 0 0 / 21%); 15 | } 16 | 17 | /* ============= Scrollbar ============= */ 18 | ::-webkit-scrollbar { 19 | width: 4px; 20 | } 21 | ::-webkit-scrollbar-track { 22 | background: #000; 23 | } 24 | ::-webkit-scrollbar-thumb { 25 | background: white; 26 | } 27 | 28 | 29 | 30 | #main { 31 | display: flex; 32 | flex-direction: column; 33 | justify-content: center; 34 | align-items: center; 35 | display: flex; 36 | } 37 | 38 | #header { 39 | width: 1400px; 40 | margin-top: 1rem; 41 | 42 | box-shadow: 10px 10px 30px rgba(0, 0, 0, 0.363); 43 | border-radius: 10px; 44 | background-color: rgba(255, 255, 255, 0.1); 45 | overflow: hidden; 46 | display: flex; 47 | justify-content: center; 48 | border-top: 1px solid rgba(255, 255, 255, 0.4); 49 | border-left: 1px solid rgba(255, 255, 255, 0.4); 50 | font-family: 'Poppins', sans-serif; 51 | backdrop-filter: blur(8px); 52 | } 53 | #header a { 54 | text-decoration: none; 55 | 56 | } 57 | #title { 58 | text-align: center; 59 | font-size: 2rem; 60 | /* color: rgb(90, 0, 90) ; */ 61 | color: rgb(244, 244, 244); 62 | padding: 1rem 0; 63 | font-weight: bold; 64 | } 65 | 66 | #container { 67 | display: flex; 68 | width: 1000px; 69 | display: flex; 70 | flex-wrap: wrap; 71 | justify-content: center; 72 | text-align: center; 73 | } 74 | 75 | .algo-container { 76 | width: 25rem; 77 | padding: 1rem; 78 | margin: 1rem; 79 | box-shadow: 10px 10px 30px rgba(0, 0, 0, 0.363); 80 | border-radius: 10px; 81 | background-color: rgba(255, 255, 255, 0.1); 82 | overflow: hidden; 83 | display: flex; 84 | justify-content: center; 85 | border-top: 1px solid rgba(255, 255, 255, 0.5); 86 | border-left: 1px solid rgba(255, 255, 255, 0.5); 87 | font-family: 'Poppins', sans-serif; 88 | backdrop-filter: blur(5px); 89 | } 90 | 91 | /* .imageContainer:hover img{ 92 | scale: calc(105%); 93 | transition: all 0.3s ease-in-out; 94 | } */ 95 | 96 | .image { 97 | width: 100%; 98 | height: 13rem !important; 99 | border-radius: 5px; 100 | } 101 | 102 | 103 | .algo-container a { 104 | text-decoration: none; 105 | color: #dcebff; 106 | font-size: 20px; 107 | margin: 0; 108 | } 109 | .algo-container a .description { 110 | box-shadow: 20px 20px 20px rgba(0, 0, 0, 0.065); 111 | border-radius: 5px; 112 | background-color: rgba(255, 255, 255, 0.062); 113 | border-top: 1px solid rgba(255, 255, 255, 0.3); 114 | border-left: 1px solid rgba(255, 255, 255, 0.3); 115 | font-family: 'Poppins', sans-serif; 116 | backdrop-filter: blur(5px); 117 | 118 | padding: 1rem; 119 | } 120 | .algo-container a .description p:nth-child(1) { 121 | margin-bottom: 0.2rem; 122 | font-size: 1.1rem; 123 | } 124 | .algo-container a .description p:nth-child(2) { 125 | font-size: 0.8rem; 126 | } 127 | 128 | @media (max-width: 1050px) { 129 | #container { 130 | width: 100%; 131 | } 132 | } 133 | @media (max-width: 1400px) { 134 | #header { 135 | width: 95%; 136 | } 137 | 138 | } 139 | 140 | 141 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Algorithm Visualizer 10 | 11 | 12 |
13 | 16 |
17 | 28 | 39 | 51 | 62 |
63 |
64 | 65 | 66 | 67 | 68 | 69 | 70 | --------------------------------------------------------------------------------