I was trying to implement merge sort algorithm in JavaScript without built in methods like slice(), splice(), etc. It doesn’t work exactly I wish. Can you help me to figure out where is bug hidden? Getting output [3, 5, 5, 3, 7] instead of [3, 5, 5, 7, 8].
// Merge Sort implementation
// sort implementation
function sort(arr, start, mid, end) {
// Creation and filling the temp arrays
let lArray = [];
let rArray = [];
for (let i = 0; i <= mid - start; i++) {
lArray[i] = arr[start + i];
}
for (let j = 0; j <= end - mid - 1; j++) {
rArray[j] = arr[mid + 1 + j];
}
// Sorting and updating current array
let i = 0;
let j = 0;
let k = start;
while (i < lArray.length && j < rArray.length) {
if (lArray[i] < rArray[j]) {
arr[k] = lArray[i];
i++;
k++;
} else {
arr[k] = rArray[j];
j++;
k++;
}
}
// Handling last element in lArray or rArray
i < lArray.length ? arr[k] = lArray[i] : arr[k] = rArray[j];
}
// Recursive Merge Sort
function recursiveMergeSort(arr, start, end) {
if (start < end) {
let mid = Math.floor(((end) + start) / 2);
//console.log(start, end, mid);
recursiveMergeSort(arr, start, mid);
recursiveMergeSort(arr, mid + 1, end);
sort(arr, start, mid, end);
}
}
function mergeSort(arr) {
let start = 0;
let end = arr.length - 1;
recursiveMergeSort(arr, start, end);
return (arr)
}
console.log(mergeSort([5, 8, 3, 7, 5]));
// Handling last element in lArray or rArray is not correct.
After merging arrays you have to check the rests and copy untreated tail if it does exist
while (i < lArray.length) {
arr[k] = lArray[i];
i++;
k++;
}
while (j < rArray.length) {
arr[k] = rArray[j];
j++;
k++;
}
Replacing ternary operator by this code gives correct results for given data
Example of a somewhat optimized version of top down merge sort. It does a one time allocation of a working array and uses a pair of mutually recursive functions to change the direction of merge for each level of recursion. Takes about 1/4 second to sort 1 million pseudo-random integers on my system:
// merge sort top down
function merge(a, b, bgn, mid, end) {
var i = bgn // left: a[bgn,mid)
var j = mid // right: a[mid,end)
var k = bgn // index for b[]
while(true){
if(a[i] <= a[j]){ // if left <= right
b[k++] = a[i++] // copy left
if(i < mid) // if not end of left
continue // continue back to while
do // else copy rest of right
b[k++] = a[j++]
while(j < end)
break // and break
} else { // else left > right
b[k++] = a[j++] // copy right
if(j < end) // if not end of right
continue // continue back to while
do // else copy rest of left
b[k++] = a[i++]
while(i < mid)
break // and break
}
}
}
function sortatob(a, b, bgn, end) { // sort a to b
if ((end-bgn) < 2){
b[bgn] = a[bgn]
return
}
var mid = Math.floor(bgn + (end - bgn) / 2)
sortatoa(a, b, bgn, mid)
sortatoa(a, b, mid, end)
merge(a, b, bgn, mid, end)
}
function sortatoa(a, b, bgn, end) { // sort a to a
if ((end-bgn) < 2)
return
var mid = Math.floor(bgn + (end - bgn) / 2)
sortatob(a, b, bgn, mid)
sortatob(a, b, mid, end)
merge(b, a, bgn, mid, end)
}
function mergesort(a) { // entry function
if(a.length < 2)
return
var b = new Array(a.length) // allocate temp array
sortatoa(a, b, 0, a.length) // start with sort a to a
}
var a = new Array(1000000)
for (i = 0; i < a.length; i++) {
a[i] = parseInt(Math.random() * 1000000000)
}
console.time('measure')
mergesort(a)
console.timeEnd('measure')
for (i = 1; i < a.length; i++) {
if(a[i-1] > a[i]){
console.log('error')
break
}
}
Related
Please find my quicksort implementation below in javascript.
const A = [4, 6, 2, 5, 7, 9, 1, 3];
const partition = function(l, h) {
let pivot = A[l];
let i = l;
let j = h;
while(A[i] <= pivot) {
i++;
}
while(A[j] > pivot) {
j--;
}
if (i < j) {
let temp = A[i];
A[i] = A[j];
A[j] = temp;
}
let temp1 = A[l];
A[l] = A[j];
A[j] = temp1;
return j;
}
const quickSort = function (l, h) {
if (l < h) {
let piIdx = partition(l, h);
console.log('the pidx is ', piIdx);
quickSort(l, piIdx - 1);
quickSort(piIdx + 1, h);
}
}
quickSort(0, A.length - 1);
console.log('array after quicksort call ', A);
The output is as below:
[ 1, 2, 3, 5, 7, 9, 6, 4 ]
The last set of elements haven't been sorted properly. Could anyone please have a look and let me know the problem.
thanks
The problem is that your partition function will at most perform 2 swaps. This cannot be right.
The process of swapping (the first one in your code) should be repeated until i has arrived at j.
Not the problem, but as the function is mutating A, that should be a parameter of the function -- that is best practice.
Here is an update of your function, with a test script below it that tests the implementation for 1000 arrays that are randomly shuffled:
const partition = function(A, l, h) { // A is parameter
let pivot = A[l];
let i = l;
let j = h;
while (true) { // Keep going
while(A[i] <= pivot) {
i++;
}
while(A[j] > pivot) {
j--;
}
if (i >= j) break; // All done
let temp = A[i];
A[i] = A[j];
A[j] = temp;
}
A[l] = A[j];
A[j] = pivot; // We already know A[l], no need for temp
return j;
}
const quickSort = function (A, l, h) { // A is parameter
if (l < h) {
let piIdx = partition(A, l, h);
quickSort(A, l, piIdx - 1);
quickSort(A, piIdx + 1, h);
}
}
// Test the implementation
function shuffle(a) {
let i = a.length;
while (i) {
let j = Math.floor(Math.random() * i--);
[a[i], a[j]] = [a[j], a[i]];
}
}
const A = [...Array(50).keys()]; // is sorted
const ref = A.toString(); // string to compare solution with
for (let attempt = 0; attempt < 1000; attempt++) {
shuffle(A);
quickSort(A, 0, A.length - 1);
if (A.toString() != ref) {
console.log('Error: array not sorted after quicksort call ', ...A);
break;
}
}
console.log("all tests done");
You are halfway there ,
you are iterating the low and high but those both along with there swap needs to be in a loop that goes from low all the way to high
meaning until high does not overlap low , the loop will keep on going .
and once the loop breaks you got your index of putting the pivot
you also need to have a pivot index that will help you at the end to swap the j with the pivot index;
so it should be like this
var pivotIndex=l;
while(i<j){
while(A[i] <= pivot) {
i++;
}
while(A[j] > pivot) {
j--;
}
if (i < j) {
let temp = A[i];
A[i] = A[j];
A[j] = temp;
}
let temp1 = A[l];
A[l] = A[j];
A[j] = temp1;
}
[nums[pivotIndex],nums[j]]=[nums[j],[nums[pivotIndex]]
return j;}
I hope this help
Am Trying to run the mergesort algorithm in p5.js but function mergesort(arr, start, end) is throwing an error (Maximum call stack size exceeded).
The output should be a sorted array diplayed as lines on the browser window.
Here is my code:
var values = []
function setup(){
createCanvas(600,400);
values.length = width;
for(let i=0; i<values.length; i++){
values[i] = random(height);
}
mergesort(values, 0, values.length-1)
}
function draw(){
background(0);
for (let i=0; i< values.length; i++){
stroke(255,0,255);
line(i, height, i, height - values[i]);
}
}
function mergesort(arr, start, end){
if(arr.length <= 1)
return;
let middle =arr.length/2
mergesort(arr, start, middle);
mergesort(arr, middle +1, end);
merge(arr, start, middle, end);
}
function merge(arr, start, middle, end){
let sizeL = middle - start;
let sizeR = middle - end;
let arrL = [];
let arrR = [];
for (let i = start; i < sizeL; i++)
arrL.push(arr[i])
for (let j = sizeR; j < end; j++)
arrR.push(arr[j])
let a = 0;
let b = 0;
let k = 1;
while (a< sizeL && b < sizeR){
if (arrL[i] <= arrR[b]){
arr[k] = arrL[a];
a++;
}
else{
arr[k] = arrR[b];
b++;
}
k++;
}
while (a < sizeL){
arr[k] = arrL[b];
a++;
k++;
}
while (b < sizeR) {
arr[k] = arrR[b];
b++;
k++;
}
}
Kindly rectify the error
The length of the array never changes, thus the condition if(arr.length <= 1) will never terminate and the result of let middle = arr.length/2 is always the same.
You have to evaluate if end - start <= 1 and compute middel by (start + end)/2:
function mergesort(arr, start, end){
if(end - start <= 0)
return;
let middle = Math.floor((start + end)/2);
mergesort(arr, start, middle);
mergesort(arr, middle+1, end);
merge(arr, start, middle, end);
}
Further more there are some issues with indexing. Note, in merge, the arguments start, middle, end are indices. Hence the lengths of the arrays are middle - start + 1 respectively end - middle. And you have to put the sorted elements starting at start, thus k = start:
function merge(arr, start, middle, end){
let sizeL = middle - start + 1;
let arrL = [];
for (let i = start; i < middle+1; i++)
arrL.push(arr[i])
let sizeR = end - middle;
let arrR = [];
for (let j = middle+1; j < end+1; j++)
arrR.push(arr[j])
let a = 0, b = 0, k = start;
// [...]
}
The code can be simplified by using Array slice() method:
let arrL = arr.slice(start, middle+1);
let arrR = arr.slice(middle+1, end+1);
See the example:
var values = []
function setup(){
createCanvas(600,400);
values.length = width;
for(let i=0; i<values.length; i++){
values[i] = random(height);
}
mergesort(values, 0, values.length-1)
}
function draw(){
background(0);
for (let i=0; i< values.length; i++){
stroke(255,0,255);
line(i, height, i, height - values[i]);
}
}
function mergesort(arr, start, end){
if(end - start <= 0)
return;
let middle = Math.floor((start + end)/2);
mergesort(arr, start, middle);
mergesort(arr, middle+1, end);
merge(arr, start, middle, end);
}
function merge(arr, start, middle, end){
let sizeL = middle - start + 1;
let arrL = arr.slice(start, middle+1);
let sizeR = end - middle;
let arrR = arr.slice(middle+1, end+1);
let a = 0, b = 0, k = start;
while (a< sizeL && b < sizeR){
if (arrL[a] <= arrR[b]){
arr[k++] = arrL[a++];
}
else{
arr[k++] = arrR[b++];
}
}
while (a < sizeL){
arr[k++] = arrL[a++];
}
while (b < sizeR) {
arr[k++] = arrR[b++];
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>
I've been working on this problem for a while and I can't get my partition function to work. I am increment the start value until it reaches a value less than the pivot and vice versa w/ my end-pointer. I swap values with my start and endpointer. Then I swap value with my start and pivot. Am I missing something?
function quickSort(arr, start, end) {
if (start >= end) {
return
}
let index = partition(arr, start, end)
quickSort(arr, start, index - 1)
quickSort(arr, index + 1, end)
return arr
}
function partition(arr, start, end) {
let pivotIndex = end
let pivotValue = arr[end]
let endPointer = arr[end - 1] //end pointer start w/ value left of pivot
while (start <= end) {
if (arr[start] < pivotValue) {
start++
} else {
swap(arr,start, endPointer)
}
if (arr[endPointer] > pivotValue) {
endPointer--
} else {
swap(arr,start, endPointer)
}
}
swap(arr,start, pivotIndex)
return start
}
function swap(arr, a, b) {
let temp = arr[a]
arr[a] = arr[b]
arr[b] = temp
}
let arr = [0,5,2,1,6,3]
console.log(quickSort(arr, 0, arr.length - 1))
The partition function appears to be a variation of Hoare. There are a few issues (endPointer should be end-1, not arr[end-1]; while (start < endPointer); if/else/swap if/else/swap should be while / while / swap / increment indexes again / ...) and by using the last value as pivot means the backwards scan will need to check if it has decremented below start. Normally Hoare relies on running into the pivot, so the pivot is chosen from the middle, or at least not from the first or last element. Here is an example of "classic" Hoare with partition code merged into the quicksort function:
function quicksort(a, lo, hi) {
if(lo >= hi)
return;
var p = a[Math.floor(lo+(hi-lo)/2)];
var i = lo - 1;
var j = hi + 1;
var t;
while (true){
while (a[++i] < p);
while (a[--j] > p);
if (i >= j)
break;
t = a[i];
a[i] = a[j];
a[j] = t;
}
quicksort(a, lo, j);
quicksort(a, j+1, hi);
}
Since the code is using the last value for the pivot value, an alternative would be Lomuto partition scheme:
function quicksort(a, lo, hi) {
if(lo >= hi)
return;
var p = a[hi];
var i = lo;
var j;
var t;
for(j = lo; j < hi; j++){
if(a[j] < p){
t = a[i];
a[i] = a[j];
a[j] = t;
i++;
}
}
t = a[i];
a[i] = a[hi];
a[hi] = t;
quicksort(a, lo, i-1);
quicksort(a, i+1, hi);
}
I'm trying to create a javascript function that shifts an array right x units any up y units. It must keep the array size the same, and it must call unloadChunk for elements that are getting shifted off the multidimensional array. Here is my current implementation:
function shift(x, y) {
if (x > 0) {
for (var i = 0; i < chunks.length; i++) {
for (var j = chunks[i].length - 1; j >= 0; j--) {
if(j + x > chunks[i].length - 1 && chunks[i][j]) {
unloadChunk(i, j);
}
if (j < x) {
chunks[i][j] = null;
}
else {
chunks[i][j] = chunks[i][j - x];
}
}
}
}
else if (x < 0) {
for (var i = 0; i < chunks.length; i++) {
for (var j = 0; j < chunks[i].length; j++) {
if(j + x < 0 && chunks[i][j]) {
unloadChunk(i, j);
}
if (j - x >= chunks[i].length) {
chunks[i][j] = null;
}
else {
chunks[i][j] = chunks[i][j - x];
}
}
}
}
if (y > 0) {
for (var i = 0; i < chunks.length; i++) {
if (i + y >= chunks.length) {
for (var j = 0; j < chunks.length; j++) {
if(i - y < 0 && chunks[i][j]) {
unloadChunk(i, j);
}
chunks[i][j] = null;
}
}
else {
for (var j = 0; j < chunks.length; j++) {
if(i - y < 0 && chunks[i][j]) {
unloadChunk(i, j);
}
chunks[i][j] = chunks[i + y][j];
}
}
}
}
else if (y < 0) {
for (var i = chunks.length - 1; i >= 0; i--) {
if (i + y < 0) {
for (var j = 0; j < chunks.length; j++) {
if(i - y > chunks.length - 1 && chunks[i][j]) {
unloadChunk(i, j);
}
chunks[i][j] = null;
}
}
else {
for (var j = 0; j < chunks.length; j++) {
if(i - y > chunks.length - 1 && chunks[i][j]) {
unloadChunk(i, j);
}
chunks[i][j] = chunks[i + y][j];
}
}
}
}
}
If you're having trouble understanding exactly what I want the shift function to do, take a look at this fiddle and look at the html output.
My attempt at creating the shift function works, but it has 10 for loops. My question was, is there a more efficient, less verbose way to do this?
This proposal uses
Array#forEach: visit each item
Array#map: return value for each item
Array#pop: removes and return last element
Array#push: adds one or more elements at the end
Array#shift: removes and return first element
Array#unshift: adds one or more elements at the beginning
For better visibillity, I replaced the null value with 1000, 2000, 3000 and 4000.
function shift(x, y) {
while (x > 0) {
chunks.forEach(function (a) {
a.pop();
a.unshift(1000);
});
x--;
}
while (x < 0) {
chunks.forEach(function (a) {
a.shift();
a.push(2000);
});
x++;
}
while (y > 0) {
chunks.unshift(chunks.pop().map(function () { return 3000; }));
y--;
}
while (y < 0) {
chunks.push(chunks.shift().map(function () { return 4000; }));
y++;
}
}
function print(msg) {
document.body.innerHTML += '<p>' + msg + '</p>';
}
function printarr(arr) {
for (var i = 0; i < arr.length; i++) {
print(JSON.stringify(arr[i]))
}
}
var chunks = [[5, 3, 1], [9, 2, 5], [2, 3, 7]];
print("chunks: " + JSON.stringify(chunks));
shift(1, 0);
print("shifting right 1. chunks: "); printarr(chunks);
shift(-1, 0);
print("shifting left 1. chunks: "); printarr(chunks);
shift(0, 1);
print("shifting up 1. chunks: "); printarr(chunks);
shift(0, -1);
print("shifting down 1. chunks: "); printarr(chunks);
You can use pop(), push(), shift(), unshift() Array methods
var chunks = [
[5, 3, 1],
[9, 2, 5],
[2, 3, 7]
];
function shiftDown(){
chuck.pop();
chuck.unshift(Array(3));
}
function shiftUp(){
chuck.shift();
chuck.push(Array(3));
}
function shiftRight(){
chuck.foreach(function(v){
v.pop();
v.unshift(null);
})
}
function shiftLeft(){
chuck.foreach(function(v){
v.shift();
v.push(null);
})
}
If interpret Question correctly, you can use Array.prototype.forEach(), Array.prototype.splice()
var chunks = [[5, 3, 1], [9, 2, 5], [2, 3, 7]];
// `x`: Index at which to start changing array
// `y`: An integer indicating the number of old array elements to remove
function shift(arr, x, y, replacement) {
arr.forEach(function(curr, index) {
// The elements to add to the array, beginning at the start index.
// start index: `x`
curr.splice(x, y, replacement)
});
return arr
}
// e.g.,
shift(chunks, -1, 1, null);
console.log(chunks);
it must call unloadChunk for elements that should be set to null
it's not a good idea to mutate an Array, while you're iterating over the same Array. So change unloadChunk() to not change chunks, but return the new value.
in my case I'm filling all the null values with new values.
then why do you bother to insert the null-values in the first place? why don't you just insert the new values?
//one-dimensional shift
function shift(arr, offset, callback){
var i, j, len = arr.length;
if(len && (offset |= 0)){
typeof callback === "function" || (callback = function(v){return v});
if(offset < 0){
for(i=-offset,j=0; i<len;)arr[j++]=arr[i++];
while(j<len)arr[j]=callback(null,j++,arr);
}else if(offset){
for(i=len-offset,j=len; i>0;)arr[--j]=arr[--i];
for(i=0; i<j;++i)arr[i]=callback(null,i,arr);
}
}
return arr;
}
//two dimensional shift
function shift2d(matrix, offsetX, offsetY, callback){
var i, len = matrix.length, tmp, fn;
offsetY |= 0;
offsetX |= 0;
if(len && matrix[0].length && (offsetY || offsetX)){
typeof callback === "function" || (callback = function(v){return v});
fn = function(val,j){ return callback(null, [i,j], matrix) };
tmp = {length: matrix[0].length};
offsetY && shift(matrix, offsetY, function(){return tmp});
for(i = 0; i < len; ++i){
if(matrix[i] === tmp){
matrix[i] = Array.from(tmp,fn);
}else{
shift(matrix[i], offsetX, fn);
}
}
}
return matrix;
}
and the code:
var chunks = [[5, 3, 1], [9, 2, 5], [2, 3, 7]];
console.log(chunks);
console.log(shift2d(chunks, -1, 1));
console.log(shift2d(chunks, 1, -1, computeValue));
function computeValue(value, index, array){
console.log("value: %o index: %o, is chunks: %o", value, index, array === chunks);
//return the new value for this field
return JSON.stringify(index);
return Math.random();
//with ES6 array destructuring:
var [row,col] = index;
return row*3 + col;
}
shift() and shift2d() expect as the last argument an (optional) callback function that returns the new values.
For consistency reasons, I pass the same argument as Array.map and the others
value: always null, only there for consistency reasons
index: the "index" that is accessed. For shift2d this is an array of indices (take a look at array destructuring)
array: the current array/matrix. Don't mess around with this while it gets changed. The main reason to pass this argument, is to check wich Array you're currently processing.
So I'm working on Khan Academy's Algorithms course, and am trying to implement a recursive merge sort in Javascript. Here is my code so far:
var mergeSort = function(array, p, r) {
if(r>p) {
var q = floor(r/2);
mergeSort(array, p, q);
mergeSort(array, q+1, r);
merge(array, p, q, r);
}
};
merge is a function provided by Khan Academy to merge the subarrays back together. It is giving me the error: 'Uncaught RangeError: Maximum call stack size exceeded'.
EDIT: More details: I am fairly sure the error is in my code, there code is purposefully obfuscated and unreadable because the user needs to implement it themselves in a later challenge.
Here is the code that actually calls the mergeSort function initially and declares the array:
var array = [14, 7, 3, 12, 9, 11, 6, 2];
mergeSort(array, 0, array.length-1);
println("Array after sorting: " + array);
Program.assertEqual(array, [2, 3, 6, 7, 9, 11, 12, 14]);
And here is the code for the merge function, although it is obfuscared as I mentioned above:
var merge = function(array, p, q, r) {
var a = [],
b = [],
c = p,
d, e;
for (d = 0; c <= q; d++, c++) {
a[d] = array[c];
}
for (e = 0; c <= r; e++, c++) {
b[e] = array[c];
}
c = p;
for (e = d = 0; d < a.length && e < b.length;) {
if (a[d] < b[e]) {
array[c] = a[d];
d++;
} else {
array[c] = b[e];
e++;
}
c++;
}
for (; d < a.length;) {
array[c] = a[d];
d++;
c++;
}
for (; e < b.length;) {
array[c] = b[e];
e++;
c++;
}
};
They also require my code inside of the mergeSort function be of the form:
if (____) {
var ____ = ____;
mergeSort(____,____,____);
mergeSort(____,____,____);
merge(____,____,____,____);
}
Mergesort is a divide and conquer algorithm which splits the range of indices to sort in two, sorts them separately, and then merges the results.
Therefore, middle variable should be the arithmetic mean of from and to, not the half of to.
I have renamed the variables to make it more understandable:
var mergeSort = function(array, from, to) {
if(to > from) {
var middle = Math.floor( (from+to)/2 ); // Arithmetic mean
mergeSort(array, from, middle);
mergeSort(array, middle+1, to);
merge(array, from, middle, to);
}
};
q is supposed to be the half way point between p and r, but you've failed to take into account that the starting point (i.e. p) might not be 0 when you do this:
var q = floor(r/2);
You need to do something like:
var q = floor((r-p)/2) + p;
Although as #Oriol points out the middle point is actually exactly the same as the arithmetic mean and so the calculation can be simplified.
Just for the sake of implementation of the merge sort in JS
function merge(arr){
if(arr.length <= 1) return arr;
let mid = Math.floor(arr.length/2);
let left = merge( arr.slice(0, mid));
let right = merge(arr.slice(mid))
function mergeSort(arr1, arr2) {
let result = [];
let i=0;
let j=0;
while(i< arr1.length && j < arr2.length) {
if(arr1[i] < arr2[j]){
result.push(arr1[i])
i++;
} else {
result.push(arr2[j])
j++;
}
}
while(i < arr1.length) {
result.push(arr1[i])
i++;
}
while(j < arr2.length){
result.push(arr2[j])
j++;
}
return result
}
return mergeSort(left,right)
}
console.log(merge([1,4,3,6,2,11,100,44]))
High level strategy
the merge sort works on the principle that it's better to sort two numbers than to sort a large list of numbers. So what it does is that it breaks down two lists into their individual numbers then compares them one to the other then building the list back up. Given a list of say 1,3,2, it'll split the list into 1 and 3,2 then compare 3 to 2 to get 2,3 and then compare the list of 2,3 to 1. If 1 is less than the first element in list of 2,3 it simply places 1 in front of the list. Just like that, the list of 1,3,2 is sorted into 1,2,3.
pseudocode-steps
1.take first member of first array
2.compare to first member of second array
3.if first member of first array is less than first member
of second array
4.put first member into sorted array
5.now merge first array minus first element
with second array
6.else take first member of second array
merge first array with remaining portion of second array
7.return sorted array
function mergesorted(list1, list2) {
let sorted = [];
if (list1.length === 0 || list2.length === 0) {
return list1.length === 0 ? list2.length === 0 ? [] : list2 : list1;
}
if (list1[0] < list2[0]) {
sorted.push(list1[0])
return sorted.concat(mergesorted(list1.slice(1), list2))
} else {
sorted.push(list2[0])
return sorted.concat(mergesorted(list1, list2.slice(1)))
}
}
console.log(mergesorted([1, 2], [3, 4])) //should: [1,2,3,4]
console.log(mergesorted([1,2], [3])) //should: [1,2,3]
Merge sort implementation, stable and in place
function sort(arr, start, end) {
if (start >= end-1) return;
var mid = start + ~~((end-start)/2);
// after calling this
// left half and right half are both sorted
sort(arr, start, mid);
sort(arr, mid, end);
/**
* Now we can do the merging
*/
// holding merged array
// size = end-start
// everything here will be copied back to original array
var cache = Array(end-start).fill(0);
var k=mid;
// this is O(n) to arr[start:end]
for (var i=start, r=0;i<mid;r++,i++) {
while (k<end && arr[k] < arr[i]) cache[r++] = arr[k++];
cache[r] = arr[i];
}
// k marks the position of the element in the right half that is bigger than all elements in the left
// effectively it tells that we should only copy start~start+k element from cache to nums
// because the rests are the same
for (var i=0;i<k-start;i++) arr[i+start]=cache[i];
}
A nice solution:
const merge = (left, right) => {
const resArr = [];
let leftIdx = 0;
let rightIdx = 0;
while (leftIdx < left.length && rightIdx < right.length) {
left[leftIdx] < right[rightIdx]
? resArr.push(left[leftIdx++])
: resArr.push(right[rightIdx++]);
}
return [...resArr, ...left.slice(leftIdx), ...right.slice(rightIdx)];
};
const mergeSort = arr =>
arr.length <= 1
? arr
: merge(
mergeSort(arr.slice(0, Math.floor(arr.length / 2))),
mergeSort(arr.slice(Math.floor(arr.length / 2)))
);
try this
const mergeTwoSortedArr = (arr1 = [], arr2 = []) => {
let i = 0,
j = 0,
sortedArr = [];
while(i < arr1.length || j < arr2.length) {
if(arr1[i] <= arr2[j] || (i < arr1.length && j >= arr2.length)) {
sortedArr.push(arr1[i]);
i++;
}
if(arr2[j] <= arr1[i] || (j < arr2.length && i >= arr1.length)) {
sortedArr.push(arr2[j]);
j++;
}
}
return sortedArr;
}
const mergeSort = (arr) => {
if(arr.length === 0 || arr.length === 1) {
return arr;
}
var mid = Math.floor(arr.length / 2);
var left = mergeSort(arr.slice(0, mid));
var right = mergeSort(arr.slice(mid));
return mergeTwoSortedArr(left, right);
}
when trying this
mergeSort([5, 2, 1, 4]) //[1, 2, 4, 5]
Time Complexity O(nlogn)
logn --> decomposition
n comaprison
Time complexity O(nlogn)