-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.js
379 lines (329 loc) · 14 KB
/
main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
/******** EVENT LISTENERS ************/
let numOfDivsCon = document.getElementById('num-of-divs-con');
let divsSelector = document.getElementById('generate-divs');
divsSelector.addEventListener('click', () => {
let num = numOfDivsCon.value;
let divHeight = container.clientWidth / num;
if(window.screen.width >= window.screen.height) {
divHeight = window.screen.height / num;
}
generateSortingDivs(num);
let sortingDivs = document.querySelectorAll('.sorting-div');
sortingDivs.forEach(d => {
d.style.height = (divHeight / 2).toString() + 'px';
});
});
let bubbleBtn = document.getElementById('bubble-sort');
bubbleBtn.addEventListener('click', () => {
let num = numOfDivsCon.value;
let divHeight = container.clientWidth / num;
if(window.screen.width >= window.screen.height) {
divHeight = window.screen.height / num;
}
generateSortingDivs(num);
let sortingDivs = document.querySelectorAll('.sorting-div');
sortingDivs.forEach(d => {
d.style.height = (divHeight / 2).toString() + 'px';
});
let divs = storeSortingDivs();
bubbleSort(divs);
});
let insertionBtn = document.getElementById('insertion-sort');
insertionBtn.addEventListener('click', () => {
let num = numOfDivsCon.value;
let divHeight = container.clientWidth / num;
if(window.screen.width >= window.screen.height) {
divHeight = window.screen.height / num;
}
generateSortingDivs(num);
let sortingDivs = document.querySelectorAll('.sorting-div');
sortingDivs.forEach(d => {
d.style.height = (divHeight / 2).toString() + 'px';
});
let divs = storeSortingDivs();
insertionSort(divs);
});
let selectionBtn = document.getElementById('selection-sort');
selectionBtn.addEventListener('click', () => {
let num = numOfDivsCon.value;
let divHeight = container.clientWidth / num;
if(window.screen.width >= window.screen.height) {
divHeight = window.screen.height / num;
}
generateSortingDivs(num);
let sortingDivs = document.querySelectorAll('.sorting-div');
sortingDivs.forEach(d => {
d.style.height = (divHeight / 2).toString() + 'px';
});
let divs = storeSortingDivs();
selectionSort(divs);
});
let quickBtn = document.getElementById('quick-sort');
quickBtn.addEventListener('click', () => {
let num = numOfDivsCon.value;
let divHeight = container.clientWidth / num;
if(window.screen.width >= window.screen.height) {
divHeight = window.screen.height / num;
}
generateSortingDivs(num);
let sortingDivs = document.querySelectorAll('.sorting-div');
sortingDivs.forEach(d => {
d.style.height = (divHeight / 2).toString() + 'px';
});
let divs = storeSortingDivs();
displayQuickSortInfo();
quickSort(divs, 0, divs.length-1);
});
/*
let mergeBtn = document.getElementById('merge-sort');
mergeBtn.addEventListener('click', () => {
let num = numOfDivsCon.value;
let divHeight = window.screen.height / num;
generateSortingDivs(num);
let sortingDivs = document.querySelectorAll('.sorting-div');
sortingDivs.forEach(d => {
d.style.height = (divHeight / 2).toString() + 'px';
});
let divs = storeSortingDivs();
displayMergeSortInfo();
mergeSort(divs);
});
*/
/********* Generate and Store Divs to be Sorted *************/
const generateSortingDivs = (numOfDivs) => {
const divContainer = document.querySelector('.div-container');
let html = '';
for (let i = 0; i < numOfDivs; i++) {
let r = Math.floor(Math.random() * 100);
html += `<div class='sorting-div' id='id-${i}' style='max-width: ${r}%'> </div>`;
}
divContainer.innerHTML = html;
}
const storeSortingDivs = () => {
const divContainer = document.querySelector('.div-container');
let divCollection = [];
const numOfDivs = divContainer.childElementCount;
for(let i=0; i<numOfDivs; i++) {
let div = document.getElementById('id-' + i);
divCollection.push(div);
}
return divCollection;
}
/******** ON INITIALIZATION ********/
let num = numOfDivsCon.value;
let container = document.querySelector('.div-container');
let divHeight = container.clientWidth / num;
if(window.screen.width >= 700) {
divHeight = window.screen.height / num;
}
generateSortingDivs(num);
let sortingDivs = document.querySelectorAll('.sorting-div');
sortingDivs.forEach(d => {
d.style.height = (divHeight/2).toString() + 'px';
});
/********** SLEEP FUNCTION ************/
//Used to allow asynchronous visualizations of synchronous tasks
function sleep() {
let ms = document.querySelector('#speed-con');
let time = ms.max - ms.value;
return new Promise(resolve => setTimeout(resolve, time));
}
/******* SWAP FUNCTIONS *********/
//Used for Testing Algorithm before Animating Visualization
const syncSwap = (div1, div2) => {
let tmp = div1.style.maxWidth;
div1.style.maxWidth = div2.style.maxWidth;
div2.style.maxWidth = tmp;
}
async function asyncSwap(div1, div2) {
await sleep();
let tmp = div1.style.maxWidth;
div1.style.maxWidth = div2.style.maxWidth;
div2.style.maxWidth = tmp;
}
/****************************************/
/*********** SORTING ALGO'S *************/
/****************************************/
/******* BUBBLE SORT ***********/
async function bubbleSort(divCollection) {
displayBubbleSortInfo();
const len = divCollection.length;
for(let i=0; i<len; i++) {
for(let j=0; j<len-i-1; j++) {
divCollection[j].style.backgroundColor = "#A45B77";
divCollection[j+1].style.backgroundColor = "#635BA4";
let numDiv1 = parseInt(divCollection[j].style.maxWidth);
let numDiv2 = parseInt(divCollection[j+1].style.maxWidth);
let div1 = divCollection[j];
let div2 = divCollection[j+1];
if(numDiv1 > numDiv2) {
await asyncSwap(div2, div1);
}
divCollection[j].style.backgroundColor = "#5ba488";
divCollection[j+1].style.backgroundColor = "#5ba488";
}
divCollection[len - i - 1].style.backgroundColor = '#5ba488';
}
}
function displayBubbleSortInfo(){
const infoDiv = document.querySelector('.algo-info');
let html = `<h1 class="main-h1">Bubble Sort Visualizer</h1>`;
html += `<h2 class="main-h1">Time Complexity: O(n^2)</h2>`;
html += `<h3 class="main-h3">Space Complexity: O(1)</h3>`;
html += `<p class="algo-text">This sorting algorithm loops through the array and continues to push the
largest found element into the last position, also pushing the last available
position down by one on each iteration. It is guaranteed to run in exactly
O(n^2) time because it is a nested loop that runs completely through.</p>`;
infoDiv.innerHTML = html;
}
/****** QUICK SORT ********/
async function quickSort(divCollection, start, end) {
if(start >= end) return;
let partitionIndex = await partition(divCollection, start, end);
await Promise.all([quickSort(divCollection, start, partitionIndex - 1), quickSort(divCollection, partitionIndex + 1, end)]);
}
/* This function takes last element as pivot, places
the pivot element at its correct position in sorted
array, and places all smaller (smaller than pivot)
to left of pivot and all greater elements to right
of pivot */
async function partition(divCollection, start, end) {
let pivotIndex = start;
let pivotValue = parseInt(divCollection[end].style.maxWidth);
for(let i = start; i < end; i++) {
if(parseInt(divCollection[i].style.maxWidth) < pivotValue) {
await asyncSwap(divCollection[i], divCollection[pivotIndex]);
pivotIndex++;
}
}
await asyncSwap(divCollection[pivotIndex], divCollection[end]);
return pivotIndex;
}
function displayQuickSortInfo(){
const infoDiv = document.querySelector('.algo-info');
let html = `<h1 class="main-h1">Quick Sort Visualizer</h1>`;
html += `<h2 class="main-h2">Time Complexity: O(n log n)</h2>`;
html += `<h3 class="main-h3">Space Complexity: O(log n)</h3>`;
html += `<p class="algo-text">This sorting algorithm uses the idea of a partition to sort
each iteration recursively. You can implement quick sort
in a variety of manners based on the method in which you
pick your "pivot" value to partition the array. In this
visualization, I implemented the method that chooses the
last element of the array as the pivot value. You could
also choose the first value, the middle value, or the median
value based on the first, middle, and last values.</p>`;
infoDiv.innerHTML = html;
}
/************* INSERTION SORT *************/
async function insertionSort(divCollection) {
displayInsertionSort();
for(let i=1; i<divCollection.length; i++) {
let j = i;
while(j > 0 && (parseInt(divCollection[j-1].style.maxWidth) > parseInt(divCollection[j].style.maxWidth))) {
await asyncSwap(divCollection[j], divCollection[j-1]);
j--;
}
}
}
function displayInsertionSort() {
const infoDiv = document.querySelector('.algo-info');
let html = `<h1 class="main-h1">Insertion Sort Visualizer</h1>`;
html += `<h2 class="main-h2">Time Complexity: O(n^2)</h2>`;
html += `<h3 class="main-h3">Space Complexity: O(1)</h3>`;
html += `<p class="algo-text">This sorting algorithm is similar to how we
like to sort a deck of cards. We iterate through
the array, and at each element we check behind it
to see where in the array it should go. This keeps
a partitioned sorted subarray in the front continuously
until we finish sorting. This algorithm does not allocate
new memory, only using swaps to sort. It has a best case
time complexity of O(n) but an average time complexity
of O(n^2).</p>`;
infoDiv.innerHTML = html;
}
/************ SELECTION SORT *************/
async function selectionSort(divCollection) {
displaySelectionSort();
for(let i=0; i<divCollection.length - 1; i++) {
let unsortedIndex = i;
let currMinIndex = i;
let unsortedNum = parseInt(divCollection[unsortedIndex].style.maxWidth);
for(let j=i+1; j<divCollection.length; j++) {
let num = parseInt(divCollection[j].style.maxWidth);
if(num < unsortedNum) {
currMinIndex = j;
unsortedNum = parseInt(divCollection[currMinIndex].style.maxWidth);
}
}
if(divCollection[unsortedIndex] !== divCollection[currMinIndex]) {
await asyncSwap(divCollection[unsortedIndex], divCollection[currMinIndex]);
}
}
}
function displaySelectionSort() {
const infoDiv = document.querySelector('.algo-info');
let html = `<h1 class="main-h1">Selection Sort Visualizer</h1>`;
html += `<h2 class="main-h2">Time Complexity: O(n^2)</h2>`;
html += `<h3 class="main-h3">Space Complexity: O(1)</h3>`;
html += `<p class="algo-text">This sorting algorithm builds a sorted subarray from
left to right, leaving the unsorted subarray on the right
side of the sorted subarray. It selects the new smallest
(or largest depending on your sort criteria) and pushes
it onto the end of the sorted subarray on each iteration.
This algorithm utilizes a simple swap and does not allocate
additional memory to perform the sort. It is a nest loop,
and therefore has a worst-case time complexity of
O(n^2), although it will run faster on average cases in
practice.</p>`;
infoDiv.innerHTML = html;
}
/************* MERGE SORT ****************/
/* Merge Sort does not sort in place, and thus we have to be
* clever when implementing it and also editing the css style
* of our divs to show the visualization of how the algorithm
* works. My method is to store a copy of the divs, that way
* I can use one to be sorted by merge sort, and the other to
* change the css style property to show the visualization.
* Unlike other sorts, we are not swapping
* elements when sorting, instead we are merging entire
* arrays together as the name implies. */
// NOT CURRENTLY WORKING VISUALLY, DOES WORK IN BACKGROUND
/*
async function mergeSort(divCollection) {
if(divCollection.length < 2) return divCollection;
let middleIndex = Math.floor(divCollection.length / 2);
let left = divCollection.slice(0, middleIndex);
let right = divCollection.slice(middleIndex);
await merge(mergeSort(left), mergeSort(right));
}
async function merge(left, right) {
let mergedCollection = [];
console.log(left);
while(left.length && right.length) {
if(parseInt(left[0].style.maxWidth) < parseInt(right[0].style.maxWidth || right.length === 0)) {
let el = left.shift();
mergedCollection.push(el);
} else {
let el = right.shift();
mergedCollection.push(el);
}
}
let res = mergedCollection.concat(left.slice().concat(right.slice()));
let divs = storeSortingDivs();
let j = 0;
for(let i = divs.indexOf(left[0]); i < mergedCollection.length; i++) {
await sleep();
divs[i].style.maxWidth = res[j].style.maxWidth;
j++
}
return res;
}
function displayMergeSortInfo() {
const infoDiv = document.querySelector('.algo-info');
let html = `<h1>Merge Sort Visualizer</h1>`;
html += `<h2>Time Complexity: O(n log n)</h2>`;
html += `<h3>Space Complexity: O(n)</h3>`;
html += `<p class="algo-text"></p>`;
infoDiv.innerHTML = html;
}
*/