Implementing merge sort iteratively - javascript

I'm trying to implement merge sort in order to get a better understanding of how it works. In the following code I am attempting to sort an array of numbers. The code I currently have is buggy and runs in an infinite loop. I'm trying to solve this non-recursively for now:
function mergeSort(arr) {
var mid = Math.floor(arr.length/2);
var left = arr.slice(0, mid);
var right = arr.slice(mid, arr.length);
if (arr.length === 1) {return arr};
var sorted = [];
var i = 0;
while (left.length || right.length) {
if (left.length && right.length) {
if (left[0] < right[0]) {
sorted.push(left.shift())
} else {
sorted.push(right.shift())
}
} else if (left) {
sorted.push(left.shift())
} else {
sorted.push(right.shift())
}
i++;
}
return sorted;
}
So if I have an array var nums = [1, 4, 10, 2, 9, 3]; calling mergeSort(nums) should return [1, 2, 3, 4, 9, 10].

You've written code that splits an array in two and merges the halves. This doesn't result in a sorted array because the two halves are not sorted. Mergesort works by sorting the two halves, then merging them.
There are many ways to implement mergesort iteratively. Let me offer one. Start by merging subarrays of size 1. You know that an array of size 1 is already sorted, so it's safe to merge two consecutive subarrays of size 1. If you do this to all consecutive pairs of subarrays of size 1 in the original array, you end up with an array consisting of consecutive sorted subarrays of size 2.
Do you see where this is going? Now you can merge every two consecutive subarrays of size 2. You end up with an array of consecutive sorted subarrays of size 4. Keep on repeating this procedure until the whole array is sorted.
The following snippet implements this approach.
function mergeSort(arr) {
var sorted = arr.slice(),
n = sorted.length,
buffer = new Array(n);
for (var size = 1; size < n; size *= 2) {
for (var leftStart = 0; leftStart < n; leftStart += 2*size) {
var left = leftStart,
right = Math.min(left + size, n),
leftLimit = right,
rightLimit = Math.min(right + size, n),
i = left;
while (left < leftLimit && right < rightLimit) {
if (sorted[left] <= sorted[right]) {
buffer[i++] = sorted[left++];
} else {
buffer[i++] = sorted[right++];
}
}
while (left < leftLimit) {
buffer[i++] = sorted[left++];
}
while (right < rightLimit) {
buffer[i++] = sorted[right++];
}
}
var temp = sorted,
sorted = buffer,
buffer = temp;
}
return sorted;
}
function print(s) {
document.write(s + '<br />');
}
var data = [1, 4, 10, 2, 9, 3];
print('input: ' + data.join(', '));
print('output: ' + mergeSort(data).join(', '));

Related

How to find the number of subarrays in an array with given sum?

My program should be as following:
Input : {1,2,3,2,1,8,-3}, sum = 5
Output should be 3 example combinations ({2,3}, {3,2}, {8,-3}) have sum
exactly equal to 5.
I tried to do it in JavaScript but I'm confused.
function findSubarraySum(arr, sum) {
var res = 0;
var currentSum = 0;
for (var i = 0; i < arr.length; i++) {
currentSum += arr[i];
if (currentSum == sum)
res++;
}
return res;
}
console.log(findSubarraySum([1, 2, 3, 4], 10));
You first need a way to iterate over all the unique ways you can choose a start and and of your subarray boundaries (your slice definition).
In my code below, I use a combinations function to get all possible combinations of two indexes for the array supplied. You could do something else, like a simple doubly nested for loop.
Next you need to take the slice of the array according to the slice definition and reduce the elements into a sum. The Array.prototype.reduce function works well for that.
Finally, you want to include the subArray in the list of results only if the reduced sum matched the desired sum.
// Input : {1,2,3,2,1,8,-3}, sum = 5
const { combinations, range } = (() => {
const _combinations = function*(array, count, start, result) {
if (count <= 0) {
yield [...result]; // Yes, we want to return a copy
return;
}
const nextCount = count - 1;
const end = array.length - nextCount; // leave room on the array for the next remaining elements
for (let i = start; i < end; i += 1) {
// we have already used the element at (start - 1)
result[result.length - count] = array[i];
const nextStart = i + 1; // Always choose the next element from the ones following the last chosen element
yield* _combinations(array, nextCount, nextStart, result);
}
};
function* combinations(array, count) {
yield* _combinations(array, count, 0, Array(count));
}
function* range(l) {
for (let i = 0; i < l; i += 1) {
yield i;
}
}
return {
combinations,
range,
};
})();
const subArraysBy = (predicate, array) => {
const result = [];
for (const [beg, end] of combinations([...range(array.length+1)], 2)) {
const subArray = array.slice(beg, end);
if (predicate(subArray)) {
result.push(subArray);
}
}
return result;
};
const sum = array => array.reduce((sum, e) => sum + e);
console.log(
subArraysBy(
a => sum(a) === 5,
[1, 2, 3, 2, 1, 8, -3],
),
);
References:
MDN: Array.prototype.reduce
MDN: function* -- not required for your solution
Lodash: _.range -- implemented this in my code rather than use the lodash one. They work similarly.
Python Docs: combinations - My combinations implementation is inspired by python itertools.

Find averages in an array of different numbers

I feel like I didn't phrase my title very well, can someone please correct it if you understand my question.
I have an array of
arr = [1,2,3,4,5,
6,7,8,9,0,
3,4,7,2,1,
4,6,1,2,3,
5,6,8,9,3
2,3,4,5,6
]
And I want to do several things
Split it into chunks with the size of 5
Calculate the number of chunks. In this case, it should be 6 chunks.
Calculate the sum of numbers of all chunks in each position and divide it by the total number of chunks. In this case,
(1+6+3+4+5+2)/6, (2+7+4+6+6+3)/6, ..., (5+0+1+3+3+6)/6
Return results as an array
var result = [3.5, 4.66, ..., 3]
I have got the idea, but not sure how to implement it.
Thanks
I believe this code accomplishes what you want.
function averageValues (arr) {
var chunks = Math.ceil(arr.length / 5); // find the number of chunks
var sums = [0, 0, 0, 0, 0]; // keep a running tally
for (var i = 0; i < arr.length; i ++) {
sums[i % 5] += arr[i]; // add each element to the proper part of the sum
}
for (var i = 0; i < sums.length; i ++) {
sums[i] /= chunks; // divide each part of the sum by the number of chunks
}
return sums;
}
You can solve this by maintaining five separate sums to end with five separate averages.
Prepare your sums array of length 5:
var sums = [ 0, 0, 0, 0, 0 ];
For each number in your set, increment the corresponding sum by that number.
for (var x = 0; x < arr.length; x++)
sums[x % 5] += arr[x];
Divide each sum by how many numbers were used:
var numbers = arr.length / 5; // 6 numbers each
var result = sums.map(
function(s) {
return s / numbers; // divide each sum by 6
}
);
This uses the assumption that your set length is always a multiple of 5.
Here is a more functional approach to your problem. This uses the assumption that your set length is always a multiple of 5.
// add extra array helpers
Array.prototype.eachSlice = function (n, fn) {
let slices = [];
for (let i = 0; i < this.length; i += n) {
let slice = this.slice(i, i + n);
slices.push(slice);
}
if (fn) slices.forEach(fn);
return slices;
}
Array.prototype.sum = function (fn) {
let fnReduce = fn ? (acc, ...args) => acc + fn(...args) : (acc, v) => acc + v;
return this.reduce(fnReduce, 0);
}
Array.prototype.avg = function (fn) {
return this.sum(fn) / this.length;
}
// actual solution
let arr = [
1,2,3,4,5,
6,7,8,9,0,
3,4,7,2,1,
4,6,1,2,3,
5,6,8,9,3,
2,3,4,5,6,
];
let chunkSize = 5;
console.log('--- question #1 ---');
console.log('Split it into chunks with the size of 5.');
console.log('-------------------');
let chunks = arr.eachSlice(chunkSize);
console.log(chunks);
console.log('--- question #2 ---');
console.log('Calculate the number of chunks. In this case, it should be 6 chunks.');
console.log('-------------------');
console.log(chunks.length);
console.log('--- question #3 ---');
console.log('Calculate the sum of numbers of all chunks in each position and divide it by the total number of chunks.');
console.log('-------------------');
let avgChunks = new Array(chunkSize).fill()
.map((_, i) => chunks.avg(chunk => chunk[i]));
console.log('See the result under question #4.');
console.log('--- question #4 ---');
console.log('Return results as an array.');
console.log('-------------------');
console.log(avgChunks);
It could be useful:
//The average method using an array
function average(arr) {
var sum = arr.reduce(function (a,b) { return a + b; },0)
return sum/ arr.length
}
//Chunk array method, it returns an array of the sliced arrays by size
function chunkArray(arr, chunkSize){
var numChunks = arr.length / chunkSize
var chunks= []
for (let index = 0; index < numChunks; index++) {
chunks.push(arr.slice(index * chunkSize, (index * chunkSize) + chunkSize))
}
return chunks
}
//Finally, the average of arrays, it returns the average of each array
function averageArrays(arrs){
return arrs.map(function (arr) {
return average(arr)
})
}
//Example of usage
var chunks = chunkArray([
1,2,3,4,5,
6,7,8,9,0,
3,4,7,2,1,
4,6,1,2,3,
5,6,8,9,3,
2,3,4,5,6
],5)
console.log(averageArrays(chunks))
I think #Aplet123 has the most straight forward and easy to understand approach, though I changed up a little bit to suit my needs.
var chunks = Math.ceil(arr.length / 5) // Find the number of chunks
var sums = new Array(5).fill(0) // Keeps a running tally and fill values 0
arr.map((x, i) => sums[i%5] += arr[i]) // add each element to the proper part of the sum
var avgs = sums.map((x) => x/chunks /divide each part of the sum by the number of chunks

looping infinitely through multiple arrays at the same time until a condition is met

I'm trying to find least common multiples of the two numbers given [3,5] and return only the number that's divisible by all the number in the range of the two numbers... for example:
The given array of two numbers --> let arr = [3,5];
The first number Multiples should be as follow:
[3,6,9,12,15,18,21,24,27,30,33,36,39,42,45,48,51,54,57,60];
The second number Multiples should be as follow:
[5,10,15,20,25,30,35,40,45,50,55,60];
The Least common multiples should be as follows:
[15,30,45,60];
the only that is divisible by all the number in the range is 60.
This is my approach to solve this problem but I want to know what's wrong with my code below (PLEASE EXPLAIN 'cause I'm tired of guessing):
let arr = [3, 5];
let arrRange = []; // [3, 4, 5]
// creating a loop to create the range
for (var i = arr[0]; i <= arr[1]; i++) {
arrRange.push(i);
}
let f = arr[0], s = arr[1], c = 0, result = 0, firstMultiples = [], secondMultiples = [], leastCommonMultiples = [];
// This function is made if the number least Common number is divisible by all the numbers in the "arrRange"
function isDivisible(num) {
for(var i = 0; i < arrRange.length; i++) {
if(num % arrRange[i] != 0) {
return false;
}
}
return true;
}
while(true) {
firstMultiples.push(f);
secondMultiples.push(s);
f = f + arr[0];
s = s + arr[1];
let vals = secondMultiples.values();
for(let val of vals){
if( firstMultiples.includes(val) ) {
leastCommonMultiples.push(val);
}
}
let cmlVals = leastCommonMultiples.values();
for(let cmlVal of cmlVals){
if(isDivisible(cmlVal)) {
result += cmlVal;
break;
}
}
c++;
}
console.log(result);
To fix it, change the while-loop from while (true) {/*code*/}; to
while(isDivisible(cmlVal) == true) {/*code*/}; and remove the
if(isDivisible(cmlVal)) {/*code*/ break;}.

Looping multiple if statements based on array length

Just started learning javascript.
Input could be something like.
1, 5, 2, 7
and my task is to figure out how many numbers is missing between the lowest number and highest number.
var sorted = statues.sort();
var ticker = 0;
var plusser = sorted[0] + 1;
var plusser1 = sorted[1] + 1;
var plusser2 = sorted[2] + 1;
var plusser3 = sorted[3] + 1;
if(sorted[1] != plusser) {
ticker++
}
if(sorted[2] != plusser1) {
ticker ++;
}
if(sorted[3] != plusser2) {
ticker ++;
}
if(sorted[4] != plusser3) {
ticker ++;
}
this works great if there only is 4 numbers of input however, that ain't always the case and i am sure this can be coded cleaner if you use some sort of loop. Can you guys help me?
Find the max and min number and loop through array and check if a number is not part of array.
var arr = [1, 5, 2, 7];
var numberMissing = 0;
for(var i = Math.min.apply(Math, arr) + 1 ; i < Math.max.apply(Math, arr); ++i){
if(arr.indexOf(i) === -1){
console.log(i);
++numberMissing;
}
}
console.log("Missing Number : " + numberMissing);
task is to figure out how many numbers is missing between the lowest number and highest number
Sort the numbers : This will give the smallest number and largest number.
Subtract largest number and smallest number : This will give total numbers that could be included that range. Lets say this is N
Subtract Array Length with N : This will give number of missing number in the given array.
Since the question is to count and not to list all the missing numbers, we can take this approach. Following is the code example.
var input = [1,5,2,7];
var sortedInput = input.sort(); // this will work only for single digit array.
var firstNum = sortedInput[0],
lastNum = sortedInput[sortedInput.length-1],
numbersInRange = lastNum - firstNum +2; // +2 to include the numbers that are the range
var missingNumbers = numbersInRange - input.length;
console.log(missingNumbers)
If the array contains unique numbers (ie - 5 can't appear twice), you can use simple math:
var statues = [1, 5, 2, 7];
var result =
Math.max.apply(Math, statues) - Math.min.apply(Math, statues) + 1 // the amount of items that should be in the array
-
statues.length; // the current amount of items
console.log(result);
If you want the numbers as well, create a map of the existing numbers, and then create an array, that contain all numbers that don't exist in the initial array:
var statues = [1, 5, 2, 7];
function getMissingNumbers(arr) {
var result = [];
var map = arr.reduce(function(map, n) { // create a map of existing numbers
map[n] = true;
return map
}, {});
var max = Math.max.apply(Math, arr); // find the max
var min = Math.min.apply(Math, arr); // find the min
for(var i = min; i < max; i++) { // run from min to max
map[i] || result.push(i); // add only numbers that don't exist in the map
}
return result;
}
var result = getMissingNumbers(statues);
console.log('Missing items: ', result);
console.log('Number of missing items: ', result.length);
Here is a simple solution you can try :
var a = [1,5,2,7];
a.sort((a, b) => a-b)
.reduce((acc, element, index) => {
if(index) acc = acc + element - a[index-1] - 1; return acc;
}, 0);

Number that represents the unique index of many arrays

I have an application that takes in multiple arrays of data each or variable length. I plan on cycling through and displaying every combination of data for each array I'm given. My first inclination was to have a single number represent the state of each array since I know the number of combinations is the product of the number of elements of each array.
So for example:
A = [0,1,2,3]
B = [0,1,2,3]
C = [0,1]
So 4 x 4 x 2 = 32 combinations I need to represent
I've managed to represent all states by applying modulo and division to a given index using each array.length. My problem is that it doesn't order well (see snippet below). Has anyone solved a similar problem or know how I could change the algorithm to get it in order?
function multiArrayIndex(index, ...args) {
var arrays = args.slice();
var output = [];
for (var i = 0, curIndex = index; i < arrays.length; i++) {
var curArray = arrays[i];
var valueIndex =(curIndex % curArray.length);
output.push(curArray[valueIndex]);
curIndex = Math.ceil(curIndex / curArray.length);
}
return output;
}
demoP = document.getElementById("demo");
for(var i = 32; i>=1; i--){
demoP.innerHTML = demoP.innerHTML + i + " - " + multiArrayIndex(i, [0,1,2,3], [0,1,2,3], [0,1] ) + "<br />";
}
<p id="demo"></p>
Keeping the indices separate would be a nicer approach in my opinion.
Incrementing the indices could work kinda similar to how we added two numbers by hand in elementary school - if an index is too large, set it to zero and increment the next one by one:
var a = [0, 1, 2, 3]
var b = [0, 1, 2, 3]
var c = [0, 1]
var state = {
a: 0,
b: 0,
c: 0
}
function increment() {
state.a++;
if (state.a >= a.length) {
state.b++;
state.a = 0;
}
if (state.b >= b.length) {
state.c++;
state.b = 0;
}
if (state.c >= c.length) {
state.c = 0;
}
console.log(state);
}
console.log(state);
<button onclick='increment()'>Increment</button>
Updating the document based on the state should be trivial from here.

Categories

Resources