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);
}
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
I'm trying to improve this could to use recursion but honestly, I'm new and I find it hard to know where exactly to user a recursion or call the function self.
function part(array, left, right){
var lArray = [];
var rArray = [];
var mid = Math.floor((left + right) / 2);
var pivot = array[mid];
for (var i = left; i <= right; i++) {
if (i !== mid) {
if (array[i] < pivot) {
} }
lArray.push(array[i]);
} else {
rArray.push(array[i]);
}
for (var i = 0; i < lArray.length; i++) {
array[left + i] = lArray[i];
}
var final = left + i;
array[final] = pivot;
for (var i = 0; i < rArray.length; i++) {
array[final + 1 + i] = rArray[i];
}
return final;
}
function sort(array, left, right) {
if (right <= left) {
return array;
}
var final = part(array, left, right); sort(array, left, final – 1); sort(array, final + 1, right);
return array;
}
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 currently working on some sorting algorithms but I got stuck at my quicksort algorithm.
Exact error:
quicksort.js:25 Uncaught RangeError: Maximum call stack size exceeded
at quicksort (quicksort.js:25)
at quicksort (quicksort.js:27)
at quicksort (quicksort.js:27)
at quicksort (quicksort.js:27)
at quicksort (quicksort.js:27)
at quicksort (quicksort.js:27)
at quicksort (quicksort.js:27)
at quicksort (quicksort.js:27)
at quicksort (quicksort.js:27)
at quicksort (quicksort.js:27)
Code:
function quicksort(array, left, right) {
if (array.length > 1) {
let sort = quicksortChange(array, left, right); //line 25
if (left < sort - 1) {
quicksort(array, left, sort - 1); //line 27
}
if (right > sort) {
quicksort(array, sort, right);
}
}
return array;
}
function quicksortChange(array, left, right) {
let pivot_number = Math.floor(array.length / 2);
let pivot = array[pivot_number];
for (i = 0; i < pivot_number; i++) {
if (array[i] > pivot) {
let a = array[i];
for (j = array.length - 1; j > pivot; j--) {
if (array[j] < pivot) {
let b = array[j];
if (a <= b) {
array[i] = b;
array[j] = a;
i++
j--
}
}
}
}
}
return i;
}
You start the algorithm with:
let cleanArray = quicksort(array, 0, array.length - 1)
Example of quicksort using classic Hoare partition algorithm:
function quicksort(array, left, right) {
if(left >= right)
return;
var pivot = array[Math.floor((left + right) / 2)];
var i = left - 1;
var j = right + 1;
while (true){
while (array[++i] < pivot);
while (array[--j] > pivot);
if (i >= j)
break;
var t = a[i];
a[i] = a[j];
a[j] = t;
}
quicksort(a, left, j);
quicksort(a, j+1, right);
}
var a = new Array(1000)
for (var i = 0; i < a.length; i++) {
a[i] = parseInt(Math.random() * 1000000000)
}
console.time('measure')
quicksort(a, 0, a.length-1)
console.timeEnd('measure')
for (var i = 1; i < a.length; i++) {
if(a[i-1] > a[i]){
console.log('error')
break
}
}
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>