Infinite loop in quicksort alogrithm - javascript

I'm wondering why i'm hitting an infinite loop in my recursive quick sort algorithm
when I try to run the return statement return (quicksort(less).concat([swapPos], quicksort(more)) it enters an infinite loop.
http://jsbin.com/uMAmIXA/1/edit
var quicksort = function (array) {
//base case
if (array.length <= 1) {
return array;
}
var pivot = Math.floor(((array.length - 1) / 2));
console.log(pivot);
var swapPos = array[pivot];
console.log(swapPos);
var less = [];
var more = [];
array = array.slice(0, swapPos).concat(array.slice(swapPos + 1));
// console.log(array);
for (i = 0; i < array.length; i++)
{
if (array[i] < swapPos) {
less.push(array[i]);
}
else {
more.push(array[i]);
}
}
//console.log(more);
// console.log(less);
// return (quicksort(less).concat([swapPos], quicksort(more)));
};
quicksort([19, 12, 44, 2, 1, 7, 85, 22, 5, 4, 3]);

array = array.slice(0,swapPos).concat(array.slice(swapPos+1));
I think this should be pivot rather than swapPos. Plus, I think you should exchange their names.

Related

JavaScript algorithm: is there a way to sort an already sorted array with the absolute values of its elements

I got this question during an interview.
I have an array that contains both negative and positive integers that are already sorted e.g.
const array = [-5, -3, 0, 2,7]
I am trying to write a function to sort the array using the absolute values of its elements. So the sorted array would be [ 0, 2, 3, 5, 7 ]
Here is my attempt
function sortArrayWithAbsoluteValue(array) {
const result = array.map(num => Math.abs(num)).sort((a,b) => a - b)
return result
}
Apparently, this works but it doesn't take advantage of the fact that the array is already sorted . Is there a better or more clever/efficient way to sort this?
the easiest solution is to introduce a new array and simply unshift it with elements from the first array
const array = [-9, -8, -5, -3, -2, 0, 2,7];
const newArray = [];
let i = 0, j = array.length - 1;
while(i <= j) {
const first = Math.abs(array[i]);
const last = Math.abs(array[j]);
if(first > last){
newArray.unshift(first)
i++;
}
else {
newArray.unshift(last)
j--;
}
}
console.log(newArray)
But this solution could be challenged by interviewers as unshift operator is slow o(n) so we can create newArray with the same size as array and then simply fill it in a similar way
const array = [-9, -8, -5, -3, -2, 0, 2,7];
const newArray = new Array(array.length);
let i = 0, j = array.length - 1, l = array.length - 1;
while(i <= j) {
const first = Math.abs(array[i]);
const last = Math.abs(array[j]);
if(first > last){
newArray[l] = first;
i++;
}
else {
newArray[l] = last;
j--;
}
l--;
}
console.log(newArray)
hope it helps!
You can let two indexes move towards eachother from either ends of the input array and based on how they compare, you copy the absolute value to the target array, filling it from end to front:
function absSorted(array) {
let result = Array(array.length);
for (let k = array.length - 1, i = 0, j = k; k >= 0; k--) {
result[k] = Math.abs(array[-array[i] < array[j] ? j-- : i++]);
}
return result;
}
const array = [-5, -3, 0, 2, 7];
console.log(absSorted(array));
You can use two iterators. One iterator starts from left and the other from right. Since the array is sorted one iterator points to the max absolute value. Store this value in a new array and iterate that iterator
const array = [-5, -3, 0, 2,7]
function f(array) {
let i = 0;
let j = array.length - 1;
const newArray = [];
while (i <= j) {
if (Math.abs(array[i]) < Math.abs(array[j])) {
newArray.push(Math.abs(array[j]));
--j;
} else {
newArray.push(Math.abs(array[i]));
++i;
}
}
return newArray;
}
console.log(f(array));
You can start at the min values with the inverted logic to get an increasing sort:
const array = [-5, -3, 0, 2, 7]
function g(array) {
let j = 0;
while (j < array.length && array[j] < 0) {
++j;
}
let i = j - 1;
const newArray = [];
while (i >= 0 && j < array.length) {
if (Math.abs(array[i]) < Math.abs(array[j])) {
newArray.push(Math.abs(array[i]));
--i;
} else {
newArray.push(Math.abs(array[j]));
++j;
}
}
if (i >= 0) {
newArray.push(...array.slice(0, i + 1).reverse().map(el => -el));
}
if (j < array.length) {
newArray.push(...array.slice(j));
}
return newArray;
}
console.log(g(array));
I converted all the numbers to the absolute value first using map
Then using a while loop, I used the indexOf and Math.min functions and the spread operator (...) to find the index of the minimum number of the array
Then I removed that from the array using splice
const array = [-5, -3, 0, 2,7];
function resort(array) {
const newArray = [];
array = array.map(i => Math.abs(i));
while (array.length) {
const minIndex = array.indexOf(Math.min(...array));
newArray.push(array.splice(minIndex, 1)[0]);
}
return newArray;
}
console.log(resort(array));

Mini-Max Sum HACKERHANK JS, why isn't working?

I don't know what's wrong, my function miniMaxSum isn't summing 1+3+4+5. At the end, the result array turns into this [ 14, 12, 11, 10 ], when it should looks like this [ 14, 13, 12, 11, 10 ]
function miniMaxSum(arr) {
let results = [];
let actualValue = 0;
let skipIndex = 0;
for (let i = 0; i < arr.length; i++) {
//skip actual index
if (i == skipIndex) continue;
actualValue += arr[i];
//restart the loop
if (i == arr.length - 1) {
skipIndex++;
results.push(actualValue);
actualValue = 0;
i = 0;
}
}
console.log(results);
console.log(Math.min(...results), Math.max(...results));
}
console.log(miniMaxSum([1, 2, 3, 4, 5]));
You're over-complicating your algorithm by trying to check whether you should add the current number to the overall sum or not. Instead, all you need to do is run a loop over your array, to sum up all your elements in your array. This will give you the total sum of all your elements. Then, again, iterate through your array. For each element in your array subtract it from the sum you just calculated and push it into a new array. This will give you the sum if you were to not use the number in the ith position. You can then find the min/max of this using JavaScript's Math.min and Math.max functions.
Here is an example using .reduce() and .map() to calculate the final result:
const miniMaxSum = arr => {
const sum = arr.reduce((s, n) => n+s, 0)
const results = arr.map(n => sum - n);
return [Math.min(...results), Math.max(...results)];
}
const [min, max] = miniMaxSum([1, 2, 3, 4, 5]);
console.log(min, max);
If you prefer standard for loops, here is an implementation of the above in a more imperative style:
const miniMaxSum = arr => {
let sum = 0;
for(let i = 0; i < arr.length; i++) { // sum all elements
sum += arr[i];
}
let results = [];
for(let i = 0; i < arr.length; i++) {
results[i] = sum - arr[i]; // sum minus the current number
}
return [Math.min(...results), Math.max(...results)];
}
const [min, max] = miniMaxSum([1, 2, 3, 4, 5]);
console.log(min, max);
Assuming you're talking about this question.
Whenever you want to restart the loop, you're setting i=0 but observe that you also have increment statement i++ in for loop so, effectively i starts from 1, not 0. You need to set i=-1 so that i=-1+1 = 0 in subsequent iteration. After doing this, you need to handle a corner case. When skipIndex==arr.length-1, check if i == arr.length-1. If yes, do results.push(actualValue); for the last value and then for loop terminates because i < arr.length is false in next iteration.
Code:
function miniMaxSum(arr) {
let results = [];
let actualValue = 0;
let skipIndex = 0;
for (let i = 0; i < arr.length; i++) {
//skip actual index
if (i == skipIndex){
if(i == arr.length - 1)
results.push(actualValue);
continue;
}
actualValue += arr[i];
//restart the loop
if (i == arr.length - 1) {
skipIndex++;
results.push(actualValue);
actualValue = 0;
i = -1;
}
}
console.log(results);
console.log(Math.min(...results), Math.max(...results));
}
miniMaxSum([1, 2, 3, 4, 5]);
Output
[ 14, 13, 12, 11, 10 ]
10 14

sort random array using only one loop without sort function

can i sort random array using only one loop without using sort function ??? but can i do the same using one for loop ? how can i do that ?
here i'm use nested loop
$(document).ready(function () {
function sortarr(arr) {
for (var i = 0; i < arr.length; i++) {
for (var j = i + 1; j < arr.length; j++) {
if (arr[i] > arr[j]) {
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
return arr;
}
console.log(sortarr([10, 18, 4, 5, 9, 6, 16, 12]));
});
You could do mergesort with one loop only:
function mergeSort(arr) {
if(arr.length < 2) return arr
const a = mergeSort(arr.slice(0, arr.length / 2)),
b = mergeSort(arr.slice(arr.length / 2));
const result = [];
while(a.length && b.length)
result.push((a[0] > b[0] ? a : b).shift());
return result.concat(a, b);
}
But as outlined in the comments above, this won't be faster than an approach with multiple loops, probably slower.
If you are interested, this is how you might implement your own quicksort.
But note that using default js sort would be much faster than any implementation that you can do yourself (most probably) as that sort is optimized to run as fast as possible. This is just an implementation of a generic textbook quicksort.
const quicksort = arr => {
const _partition = (arr, begin, end) => {
let pivot = begin;
for (let i = begin; i <= end; i++) {
if (arr[i] < arr[begin]) {
pivot++;
[arr[pivot], arr[i]] = [arr[i], arr[pivot]];
}
}
[arr[begin], arr[pivot]] = [arr[pivot], arr[begin]];
return pivot;
}
const _quicksort = (arr, begin, end) => {
if (begin < end) {
const pivot = _partition(arr, begin, end);
_quicksort(arr, begin, pivot - 1);
_quicksort(arr, pivot + 1, end);
}
}
_quicksort(arr, 0, arr.length);
}
const arr = [10, 18, 4, 5, 9, 6, 16, 12];
quicksort(arr);
console.log(arr);
And you can make it a little bit faster (in some cases) by randomizing the pivot selection like this.
const quicksort = arr => {
const _partition = (arr, begin, end) => {
let pivot = begin;
for (let i = begin; i <= end; i++) {
if (arr[i] < arr[begin]) {
pivot++;
[arr[pivot], arr[i]] = [arr[i], arr[pivot]];
}
}
[arr[begin], arr[pivot]] = [arr[pivot], arr[begin]];
return pivot;
}
const _randomizedPartition = (arr, begin, end) => {
const index = Math.floor(Math.random() * (end-begin)) + begin;
[arr[begin], arr[index]] = [arr[index], arr[begin]];
return _partition(arr, begin, end);
}
const _quicksort = (arr, begin, end) => {
if (begin < end) {
const pivot = _randomizedPartition(arr, begin, end);
_quicksort(arr, begin, pivot - 1);
_quicksort(arr, pivot + 1, end);
}
}
_quicksort(arr, 0, arr.length);
}
const arr = [10, 18, 4, 5, 9, 6, 16, 12];
quicksort(arr);
console.log(arr);
Here's a chart showing various sorting algorithms and their complexities:
A single pass through a loop without recursion or nesting is O(n), so no, not in the worst case. There are other techniques for sorting that can be incorporated to sort an array using a single loop as Jonas W. shows, but their time complexity isn't necessarily as good as good old .sort() which is Quicksort.
you can sort a random array using only one loop without using sort function
a = [10, 18, 4, 5, 9, 6, 16, 12];
for(i=0; i< a.length-1;(a[i]>a[i+1]) ? ((a[i]=a[i]^a[i+1]) && (a[i+1]=a[i]^a[i+1]) && (a[i]=a[i]^a[i+1]) && (i=0)): i++) {}
console.log(a); // 4, 5, 6, 9, 10, 12, 16, 18

Why this Javascript quicksort method only return one character in an array

var arr = [4, 5, 6, 3, 4, 5, 2, 5, 6, 4, 2,];
function quickSort(arra) {
if (arra.length <= 1) {
return arra;
}
else {
var len = arra.length;
var left = [];
var right = [];
var temp = arra.pop();
var newarr = [];
for (var i = 1; i < len; i++) {
if (arra[i] < temp) {
left.push(arra[i]);
}
else { right.push[i]; }
}
}
return newarr.concat(quickSort(left), temp, quickSort(right));
}
console.log(quickSort(arr))
The result is:
I wonder why this method only return me one character in the array?
pop() method removes last element from array (so length decreases by 1) and first element has index 0 so you need to replace your for loop with:
for (var i = 0; i < len-1; i++) {
Also you need to change right.push[i] as mentioned in comments.

Better way to turn a one-dimensional array into multi-dimensional array by required group size in JS

I am rather new to JS and I was working on a problem that asked to split an array (first argument) into groups the length of size (second argument) and returns them as a multidimensional array.
I got the problem to work right for all test cases but it suggested using the array `push()` method. I tried it multiple times and couldn't ever get it to work right. I think I was getting messed up with arrays being by reference. I eventually declared a new Array for each element. I went with a more classic deep copy each element at a time. I Didn't go back and try the `push()` method again. There has to be a more efficient way to do this. I want to write good code. Would love to see better versions please.
Thanks!
function chunk(arr, size) {
var group = 0;
var counter = 0;
var even = false;
var odd = false;
if (arr.length % size === 0) {
group = arr.length / size;
even = true;
} else {
group = Math.ceil(arr.length / size);
odd = true;
}
var newArr = new Array(group);
for (var i = 0; i < group; i++) {
newArr[i] = new Array(size);
}
for (i = 0; i < group; i++) {
for (var j = 0; j < size && counter < arr.length; j++) {
newArr[i][j] = arr[counter++];
}
}
return newArr;
}
chunk(['a', 'b', 'c', 'd'], 2);
Using Array.prototype.slice, the function can be written in a shorter way:
function chunk(array, size) {
var result = []
for (var i=0;i<array.length;i+=size)
result.push( array.slice(i,i+size) )
return result
}
You can try the slice method from the Array object. Here's an idea on how to use it.
var arr = [1, 2, 3, 4, 5, 6];
var newArr = [];
newArr.push(arr.slice(0, arr.length / 2));
newArr.push(arr.length / 2, arr.length);
This is just an shallow implementation but you can use the same concept inside a better written function.
Here's an example function:
var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
function toChunks(arr, size) {
var i = 0,
chunks = [];
for (; i < arr.length; i += size) {
chunks.push(arr.slice(i, i + size););
}
return chunks;
}
toChunks(arr, 2);

Categories