├── 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 | [](https://opensource.org/licenses/MIT)
4 | [](http://makeapullrequest.com)
5 | [](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 |
39 |
40 |
41 | Home Page
42 |
43 |
44 |
45 |
46 |
47 |
48 | Trie
49 |
50 |
51 |
52 |
53 | Binary Search Tree
54 |
55 |
56 |
57 |
58 |
59 |
60 | Path Finding
61 |
62 |
63 |
64 | Sorting
65 |
66 |
67 |
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 |
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 |
32 |
36 |
40 |
44 |
48 |
49 |
50 |
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 | Sort
42 | Pause
43 | Stop
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 |
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 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------