i am working on a sorting visualizer and i am trying to visualize the merge sorting algo but whenever i call the mergeSort() function, i always get a "maximum call stack size exceeded" error. i have tried to rectify the problem but to no avail.
document.querySelector(".merge").addEventListener("click", mergeSort);
var el = document.querySelectorAll(".bar");
var low = 0;
var high = el.length - 1
function mergeSort(){
if (low >= high){
return;
}
var mid = parseInt((low + high)/2);
mergeSort(el, low, mid);
mergeSort(el, mid + 1, high);
merge(el, low, mid, high);
}
function merge(arr, low, mid, high){
var n = mid - low + 1;
var m = high - mid;
var leftArray = new Array(n);
var rightArray = new Array(m);
for(var i = 0; i < n; i++){
leftArray[i] = arr[low + i];
}
for(var j = 0; j < m; j++){
rightArray[j] = arr[mid + 1 + j];
}
var i = 0;
var j = 0;
var k = low;
while (i < n && j < m){
if (leftArray[i].offsetHeight <= rightArray[j].offsetHeight){
arr[k].offsetHeight = leftArray[i].offsetHeight
i++;
}else{
arr[k].offsetHeight = rightArray[j].offsetHeight;
j++;
}
k++;
}
while (i < n){
arr[k].offsetHeight = leftArray[i].offsetHeight
i++;
k++;
}
while (j < m){
arr[k].offsetHeight = rightArray[j].offsetHeight;
j++;
k++;
}
}
You should define el, low and high as arguments in the function mergeSort:
function mergeSort(el, low, high) {
...
}
And you should call the function this way:
mergeSort(el, 0, el.length - 1);
The middle index can be computed with integer arithmetics:
var mid = low + ((high - low) >> 1);
Finally the merge function does not sort the array el, it modifies the offsetHeight properties of the array elements, which may or may not be relevant.
I have a recursive function below and I was just wondering how can I create an iterative (i.e. loops without recursion) for the same thing. I would really appreciate any help or suggestions thank you!
function countPalindromes(string, count) {
if (string.length <= 1) {
return count;
}
let [ firstLetter ] = string;
let lastLetter = string[string.length - 1];
if (firstLetter === lastLetter) {
let stringWithoutFirstAndLastLetters = string.substring(1, string.length - 1);
return countPalindromes(stringWithoutFirstAndLastLetters, count + 1);
} else {
return count;
}
}
console.log(countPalindromes("level", 0));
console.log(countPalindromes("aya", 0));
Took me a while, but this should work.
function countPalindromes(string, count) {
for (i = 0; i < Math.floor(string.length/2); i++) {
if (string.charAt(i) === string.charAt(string.length - i-1)) {count++};
}
return count;
}
console.log(countPalindromes("level", 0));
console.log(countPalindromes("aya", 0));
You can do something like this:
function countPalindromes(string) {
let count = 0;
for (let i = 0; i < Math.floor(string.length / 2); i++) {
const letterFromTheStart = string[i];
const letterFromTheEnd = string[(string.length - 1) - i];
if (letterFromTheStart !== letterFromTheEnd) {
break;
}
count++;
}
return count;
}
console.log(countPalindromes("level"));
console.log(countPalindromes("aya"));
The important parts are i < Math.floor(string.length / 2) and (string.length - 1) - i.
The first one assures the loop stops before or at the half of the string and the second gets the nth character starting from the string's end.
I didn't get your recursive palindrome function. Just converted the recursive one to iterative one.
function countPalindromes(string, count) {
if (string.length <= 1) {
return count;
}
let i = 0, j = string.length-1;
for (; i < j && string[i] == string[j]; i++, j--);
return i;
}
console.log(countPalindromes("level", 0));
console.log(countPalindromes("aya", 0));
You can reverse the string and compare letters until you hit a mismatch returning the last index that matched plus one.
function isPalindrome(word) {
const drow = word.split('').reverse();
for (i = 0; i < drow.length && drow[i] === word[i]; i++);
return i;
}
console.log(isPalindrome("level"));
Since I am still fairly new to js I thought it couldn't hurt to ask more experienced coders about ways to improve my coding habits and to learn efficient basics.
So im wondering if I could run, say 2 lines of code in a loop x amount of times and then x amount of times on the rest of the block.
So instead of this:
for (let i = 0; i <= 10; i++) {
this.shapes[i].x -= 1;
this.shapes[i].draw(this.ctx);
}
for (let i = 0; i <= 10; i++) {
this.shapes[i].x += 1;
this.shapes[i].draw(this.ctx);
}
Does something like this exist?
for (let i = 0; i <= 10; i++) {
//run this section i amount of times
this.shapes[i].x -= 1;
this.shapes[i].draw(this.ctx);
//then run this i amount of times
this.shapes[i].x += 1;
this.shapes[i].draw(this.ctx);
}
The only difference between the two loops' bodies seems to be one statement.
You can use some math to determine the index and some logical statements to determine if that value should be incremented or decremented, here is an example:
for (let i = 0; i <= 21; i++) {
const index = i % 11;
this.shapes[index].x += (i > 10) ? 1 : -1;
this.shapes[index].draw(this.ctx);
}
If it's exactly the same code you can refactor it like this:
for (let delta of [-1, +1]) {
for (let i = 0; i <= 10; i++) {
this.shapes[i].x += delta;
this.shapes[i].draw(this.ctx);
}
}
Another option is to use a function using delta as a parameter
changeShapeByDelta = (delta) => {
for (let i = 0; i <= 10; i++) {
this.shapes[i].x += delta;
this.shapes[i].draw(this.ctx);
}
}
changeShapeByDelta(-1);
changeShapeByDelta(+1);
Another option is to deep copy your initial shapes and restore it after the first draw.
You could ofc. define more variables then i:
for (let i = 0, j=0; i <= 10; i++, j+= 2) {
console.log(i, j);
}
Or use another var in the parent scope:
let j = 0;
for (let i = 0, j=0; i <= 10; i++, j+= 2) {
j += 2
console.log(i, j);
}
Or just use plain old if,else and break statements. Code does not have to look nice all the time.
You can loop 20 times instead of 10 times and then run first code if i<10 else run second. Below is example with simple logging.
for(let i = 0;i<22;i++){
if(i<11) console.log('first')
else console.log('second')
}
how can I optimize the incredibly slow code shown below to speed? I think, JS converts the ints to floats and vise-versa all the time. How can I spot such performance killers? I think, there is much space left to optimize :-). I think, there is a better way to handle the nibble values.
At first, I tried to ditch the &15 operation for the higher nibble. That just slowed more down instead of speed up.
(The code is incomplete. It is just a part taken from a bigger one. It is filling a buffer for createBufferSource(). It should be processed an played as soon as possible after a click event. Currently, on a mobile, it takes up to 2s for an average audio file.)
for (var len = adpcm.length, i = 0; i < len; i++) {
n = [
adpcm.charCodeAt(i) & 15,
(adpcm.charCodeAt(i) >> 4) & 15
];
for (var j = 0; j < 2; j++) {
s = n[j];
stp = imaStepTable[index];
diff = stp >> 3;
if (s & 4) diff += stp;
if (s & 2) diff += (stp >> 1);
if (s & 1) diff += (stp >> 2);
if (s & 8) diff = 0 - diff;
pred += diff;
if (pred > 32760) pred = 32760;
else if (pred < -32760) pred = -32760; //limiter
index += imaIndexTable[s];
if (index < 0) index = 0;
else if (index > 88) index = 88;
for (var k = 0; k < sampleScale; k++) { //resampler
if (!k) {
lPcm[c] = pred / 32768;
rPcm[c] = (c - shift) < 0 ? 0 : lPcm[(c - shift)];
} else {
lPcm[c] = lPcm[c - k];
rPcm[c] = rPcm[c - k];
}
c++;
}
}
}
I have written a terribly slow function for generating codes that go from AA000 to ZZ999 (in sequence not random). And I have concluded that there has to be a better way to do this. Any suggestions on how to make this faster?
function generateAlphaNumeric(){
theAlphabet = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];
resultArrray = [];
resultArrray2 = [];
teller = 0;
for(i in theAlphabet){
for(x in theAlphabet){
resultArrray[teller] = theAlphabet[i] + theAlphabet[x];
teller++;
}
}
teller = 0;
for(x = 0; x<10; x++){
for(y = 0; y<10; y++){
for(z = 0; z<10; z++){
resultArrray2[teller] = x.toString() + y.toString() +z.toString();
teller++;
}
}
}
teller = 0;
finalArray = [];
for(index in resultArrray){
for(i in resultArrray2){
finalArray[teller] = resultArrray[index] + resultArrray2[i];
teller++;
}
}
//console.log(resultArrray);
//console.log(resultArrray2);
console.log(finalArray);
}
This should be considerably faster:
var theAlphabet = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',
'P','Q','R','S','T','U','V','W','X','Y','Z'];
var theDigits = ['0','1','2','3','4','5','6','7','8','9'];
var result = [];
for (var i=0 ; i<26 ; i++) {
var prefix1 = theAlphabet[i];
for (var j=0 ; j<26; j++) {
var prefix2 = prefix1 + theAlphabet[j];
for(var x = 0; x<10; x++){
var prefix3 = prefix2 + theDigits[x];
for(var y = 0; y<10; y++){
var prefix4 = prefix3 + theDigits[y];
for(var z = 0; z<10; z++){
result.push(prefix4 + theDigits[z]);
}
}
}
}
}
Key ideas:
Generate everything in one run
Reuse partial strings as much as possible
However, I don't see how such an exhaustive list is useful. There are exactly 26 * 26 * 1000 different codes. So instead of maintaining an array with all codes it could make sense to simply build a function that generates the specific code requested:
function getCode(number) {
var z = number % 10;
number -= z; number /= 10;
var y = number % 10;
number -= y; number /= 10;
var x = number % 10;
number -= x; number /= 10;
var a = number % 26;
number -= a; number /= 26;
var b = number;
return theAlphabet[a] + theAlphabet[b] + theDigits[x] + theDigits[y] + theDigits[z];
}
function generate() {
var str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
array = [];
for (var i = 0; i < str.length; i++) {
for (var j = 0; j < str.length; j++) {
for (var k = 0; k < 10; k++) {
for (var l = 0; l < 10; l++) {
for (var m = 0; m < 10; m++) {
ar.push(str[i] + str[j] + k + l + m);
}
}
}
}
}
return array;
}
console.log(generate());
This will generate a array of all the codes .. U can save that array and parse it easily using a loop.
Try this solution:
function generate() {
var str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
ar = [];
for (var index1 = 0; index1 < str.length; index1++) {
for (var index2 = 0; index2 < str.length; index2++) {
for (var index3 = 0; index3 < 1000; index3++) {
ar.push(str[index1] + str[index2] + ('000' + index3).slice(-3));
}
}
}
return ar;
}
console.log(generate());
I didn't test it, but it should do the trick
function generateAlphaNumeric()
{
var theAlphabet = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];
var result = [];
// Will take a random letter inside theAlphabet
// Math.floor(Math.random() * theAlphabet.length) will generate a random number between 0 and 25
var i = 0;
while(i<2)
{
var letter = theAlphabet[Math.floor(Math.random() * theAlphabet.length)];
result.push(letter);
i++;
}
i = 0;
while(i<3)
{
// Adds a random number between 0 and 9
result.push(Math.floor(Math.random() * 10));
i++;
}
return result;
}
From a computational complexity perspective, unfortunately this is the best you can do. From a sheer number of instructions perspective, you can do a bit better (as others have pointed out), but it's still going to be the same order of complexity (remember that constants / multipliers are irrelevant in big-O complexity). You can also optimize the storage a bit.
Think about it. Your array needs to have 26 * 26 * 10 * 10 * 10 members. This means you need to at least touch that many elements.
Let N = number of elements in the alphabet
Let M = number of elements in your digit queue
Best Case Order Complexity = O(N * N * M * M * M) (if all you had to do was assign values)
Best case storage complexity = same as above (you have to store all the codes)
Right now you are using the following operations:
for(i in theAlphabet){ // *O(N)*
for(x in theAlphabet){ // *O(N)*
resultArrray[teller] = theAlphabet[i] + theAlphabet[x];// *(O(1))*
}
}
for(x = 0; x<10; x++){ // O(M)
for(y = 0; y<10; y++){ // O(M)
for(z = 0; z<10; z++){ // O(M)
resultArrray2[teller] = x.toString() + y.toString() +z.toString(); // O(1) (technically this is O(length of x + y + z)
teller++;
}
}
}
for(index in resultArrray){ // O(N * N)
for(i in resultArrray2){ // O(M * M * M(
finalArray[teller] = resultArrray[index] + resultArrray2[i]; //O(1)
teller++;
}
}
So at the end of the day your order complexity is O(N * N * M * M * M), which is the best you can do.
The bigger question is why you want to generate all the codes at all. If all you want is to create a unique code per order number or something, you can make a state machine like:
function getNextCode(previousCode) {
// in here, just increment the previous code
}
If all you want is a random identifier, consider using a hash of the timestamp + something about the request instead.
If you don't care about uniqueness, you can always just generate a random code.
All of the above are O(1).