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
Related
I have a function to bubble sort and I want to save the array after each swap into another array. The bubble sort is working properly and I can log the array after each swap to the console. But I cant seem to push to the other array properly.
Here's my code :
var arr = [5, 4, 3, 2, 1];
var steps = [];
function bubbleSort() {
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
}
var temp = arr;
console.log(temp);
steps.push(temp);
}
}
console.log(steps);
}
const swap = (a, x, y) => {
var temp = a[x];
a[x] = a[y];
a[y] = temp;
};
bubbleSort();
Here's a screenshot of the console :
screenshot of console
It's only when I try to use get a hold of the array at each step that it isn't showing properly? what do I do?
I think clonning the array could work ? var temp = [...arr];
var arr = [5, 4, 3, 2, 1];
var steps = [];
function bubbleSort() {
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
}
var temp = [...arr];
console.log(temp);
steps.push(temp);
}
}
console.log(steps);
}
const swap = (a, x, y) => {
var temp = a[x];
a[x] = a[y];
a[y] = temp;
};
bubbleSort();
You are inserting the Reference of the Temp-Array and not the actual content. This way, you store multiple times the reference and at the End you are presented with all those reference pointing to the last version of your temp Array.
You can use the destructuring assignment of an array, to create an easy shallow copy to be stored.
var arr = [5, 4, 3, 2, 1];
var steps = [];
function bubbleSort() {
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
}
var temp = arr;
console.log(temp);
// Changed to destructuring assignment
steps.push([...temp])
}
}
console.log(steps);
}
const swap = (a, x, y) => {
var temp = a[x];
a[x] = a[y];
a[y] = temp;
};
bubbleSort();
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
}
}
I am trying to solve this problem in JavaScript:
Given an array and a value in JavaScript, find if there is a triplet in array whose sum is equal to the given value. If there is such a triplet present in array, then print the triplet and return true. Else return false.
Now, I wrote some code, and for some reason, it's not working properly.
Here is the code:
A = [1, 4, 45, 6, 10, 8];
sum = 15;
x = A.length;
function find3Numbers(A, x, sum) {
for (i=0; i<(x-2); i++) {
for (j=i+1; j<(x-1); j++) {
for (k=j+1; x; k++) {
if (A[i] + A[j] + A[k] == sum) {
console.log(A[i]);
console.log(A[j]);
console.log(A[k]);
return true
}
return false
}
}
}
}
console.log(find3Numbers(A, x, sum));
Now when I run the code, I get a "false" message.
Any idea, why is that?
You immediately return false if the first triplet you try does not match, when you should only do so after all loops have finished.
A = [1, 4, 45, 6, 10, 8];
sum = 15;
x = A.length;
function find3Numbers(A, x, sum) {
for (i = 0; i < (x - 2); i++) {
for (j = i + 1; j < (x - 1); j++) {
for (k = j + 1; x; k++) {
if (A[i] + A[j] + A[k] == sum) {
console.log(A[i]);
console.log(A[j]);
console.log(A[k]);
return true
}
}
}
}
return false;
}
console.log(find3Numbers(A, x, sum));
Consider sorting the array beforehand for O(n^2) time complexity rather than O(n^3).
Note: O(nlog(n) + n^2) is asymptotically the same as O(n^2).
const find3Numbers = (nums, nums_length, target) => {
nums.sort((a, b) => a - b);
let i = 0;
while (i < nums_length - 2) {
let j = i + 1;
let k = nums_length - 1;
while (j < k) {
curr_sum = nums[i] + nums[j] + nums[k];
if (curr_sum < target) {
j++;
while (j < k && nums[j] == nums[j - 1]) {
j++;
}
} else if (curr_sum > target) {
k--;
while (j < k && nums[k] == nums[k + 1]) {
k--;
}
} else {
return [nums[i], nums[j], nums[k]];
}
}
i++;
while (i < nums_length - 2 && nums[i] == nums[i - 1]) {
i++;
}
}
return [-1, -1, -1];
}
const A = [1, 4, 45, 6, 10, 8];
const x = A.length;
const sum = 15;
console.log(find3Numbers(A, x, sum));
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);
}
it's a simple code, but returns different results in Andriod and Iphone.
var str = [1,2,3,4,5].sort(function () {
return -1;
})
document.write(str);
In MDN(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) it says
If compareFunction(a, b) is less than 0, sort a to a lower index than b, i.e. a comes first.
If compareFunction(a, b) returns 0, leave a and b unchanged with respect to each other, but sorted with respect to all different elements. Note: the ECMAscript standard does not guarantee this behaviour, and thus not all browsers (e.g. Mozilla versions dating back to at least 2003) respect this.
If compareFunction(a, b) is greater than 0, sort b to a lower index than a.
compareFunction(a, b) must always return the same value when given a specific pair of elements a and b as its two arguments. If inconsistent results are returned then the sort order is undefined.
So the result should be 1,2,3,4,5.
But is Iphone it shows 5,4,3,2,1
Here is a link for you to try this code. http://www.madcoder.cn/demos/ios-test.html
And after I did more and more test. I found Iphone is doing a different sorting.
Here is a link shows how sort works: http://www.madcoder.cn/demos/ios-test2.html
The javascript engines use different algorithms for their sort function. Since the compare function doesn't compare values, you get the result of the inner workings of the different algorithms instead of having a sorted result.
Looking at the source code of V8 engine (Chrome) and JavaScriptCore (which seems to be used by Safari, or at least the sort function gives the same result, so I guess it uses the same kind of algorithm), you can view the functions that are being used.
Not that it might not be exactly the functions used, what's important is that the algorithms are different. They give the same result if you actually compare values, but if not, the result is dependent on the way they operate, not on the function itself. At least not completely.
Here's V8 engine sorting function. You'll see that for arrays bigger than 10 elements, the algorithm isn't the same, so the result for arrays smaller than 10 elements is different than for those bigger than 10 elements.
You can find following algorithms here: https://code.google.com/p/chromium/codesearch#chromium/src/v8/src/js/array.js&q=array&sq=package:chromium&dr=C
comparefn = function(a, b) {
return -1
}
var InsertionSort = function InsertionSort(a, from, to) {
for (var i = from + 1; i < to; i++) {
var element = a[i];
for (var j = i - 1; j >= from; j--) {
var tmp = a[j];
var order = comparefn(tmp, element);
if (order > 0) {
a[j + 1] = tmp;
} else {
break;
}
}
a[j + 1] = element;
}
console.log(a);
}
var GetThirdIndex = function(a, from, to) {
var t_array = new InternalArray();
// Use both 'from' and 'to' to determine the pivot candidates.
var increment = 200 + ((to - from) & 15);
var j = 0;
from += 1;
to -= 1;
for (var i = from; i < to; i += increment) {
t_array[j] = [i, a[i]];
j++;
}
t_array.sort(function(a, b) {
return comparefn(a[1], b[1]);
});
var third_index = t_array[t_array.length >> 1][0];
return third_index;
}
var QuickSort = function QuickSort(a, from, to) {
var third_index = 0;
while (true) {
// Insertion sort is faster for short arrays.
if (to - from <= 10) {
InsertionSort(a, from, to);
return;
}
if (to - from > 1000) {
third_index = GetThirdIndex(a, from, to);
} else {
third_index = from + ((to - from) >> 1);
}
// Find a pivot as the median of first, last and middle element.
var v0 = a[from];
var v1 = a[to - 1];
var v2 = a[third_index];
var c01 = comparefn(v0, v1);
if (c01 > 0) {
// v1 < v0, so swap them.
var tmp = v0;
v0 = v1;
v1 = tmp;
} // v0 <= v1.
var c02 = comparefn(v0, v2);
if (c02 >= 0) {
// v2 <= v0 <= v1.
var tmp = v0;
v0 = v2;
v2 = v1;
v1 = tmp;
} else {
// v0 <= v1 && v0 < v2
var c12 = comparefn(v1, v2);
if (c12 > 0) {
// v0 <= v2 < v1
var tmp = v1;
v1 = v2;
v2 = tmp;
}
}
// v0 <= v1 <= v2
a[from] = v0;
a[to - 1] = v2;
var pivot = v1;
var low_end = from + 1; // Upper bound of elements lower than pivot.
var high_start = to - 1; // Lower bound of elements greater than pivot.
a[third_index] = a[low_end];
a[low_end] = pivot;
// From low_end to i are elements equal to pivot.
// From i to high_start are elements that haven't been compared yet.
partition: for (var i = low_end + 1; i < high_start; i++) {
var element = a[i];
var order = comparefn(element, pivot);
if (order < 0) {
a[i] = a[low_end];
a[low_end] = element;
low_end++;
} else if (order > 0) {
do {
high_start--;
if (high_start == i) break partition;
var top_elem = a[high_start];
order = comparefn(top_elem, pivot);
} while (order > 0);
a[i] = a[high_start];
a[high_start] = element;
if (order < 0) {
element = a[i];
a[i] = a[low_end];
a[low_end] = element;
low_end++;
}
}
}
if (to - high_start < low_end - from) {
QuickSort(a, high_start, to);
to = low_end;
} else {
QuickSort(a, from, low_end);
from = high_start;
}
}
};
InsertionSort([1, 2, 3, 4, 5], 0, 5);
//QuickSort is recursive and calls Insertion sort, so you'll have multiple logs for this one
QuickSort([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], 0, 13);
//You'll see that for arrays bigger than 10, QuickSort is called.
var srt = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13].sort(function() {
return -1
})
console.log(srt)
And JavaScriptCore uses merge sort. You can find this algorithm here:
http://trac.webkit.org/browser/trunk/Source/JavaScriptCore/builtins/ArrayPrototype.js
function min(a, b) {
return a < b ? a : b;
}
function merge(dst, src, srcIndex, srcEnd, width, comparator) {
var left = srcIndex;
var leftEnd = min(left + width, srcEnd);
var right = leftEnd;
var rightEnd = min(right + width, srcEnd);
for (var dstIndex = left; dstIndex < rightEnd; ++dstIndex) {
if (right < rightEnd) {
if (left >= leftEnd || comparator(src[right], src[left]) < 0) {
dst[dstIndex] = src[right++];
continue;
}
}
dst[dstIndex] = src[left++];
}
}
function mergeSort(array, valueCount, comparator) {
var buffer = [];
buffer.length = valueCount;
var dst = buffer;
var src = array;
for (var width = 1; width < valueCount; width *= 2) {
for (var srcIndex = 0; srcIndex < valueCount; srcIndex += 2 * width)
merge(dst, src, srcIndex, valueCount, width, comparator);
var tmp = src;
src = dst;
dst = tmp;
}
if (src != array) {
for (var i = 0; i < valueCount; i++)
array[i] = src[i];
}
return array;
}
console.log(mergeSort([1, 2, 3, 4, 5], 5, function() {
return -1;
}))
Again these may not be exactly the functions used in each browser, but it shows you how different algorithms will behave if you don't actually compare values.