indexes around a range of values - javascript

Considering a sorted array arr
1- Simple case:
var arr = [3, 6, 12, 18];
indexesAround(6)
//> [1, 1]
indexesAround(7)
//> [1, 2]
2- More complex case:
var arr = [3, 3, 6, 6, 18, 18];
indexesAround(6)
//> [2, 3]
indexesAround(7)
//> [3, 4]
How would you implement(or pseudo code) a such indexesAround(value) function ?
--
Here is what I have for now, but I think this could be enhanced:
function indexesAround(val) {
var lower = 0;
var upper = lower;
var el;
for (var i = 0, len = arr.length; i < len; i++) {
el = arr[i];
if (el > val) {break;}
if (arr[lower] < el) {lower = upper = i;}
if (arr[upper] <= el) {upper = i;}
}
return [lower, upper];
}

Considering the array is sorted:
function indexesAround(arr, val) {
if (!~arr.indexOf(val)) return false; // not found
var start = arr.indexOf(val);
var end = (arr.length - 1) - arr.reverse().indexOf(val);
arr.reverse(); // restore original order
return [start, end];
}

This solution covers every possibility and works exactly to OP's specifications. Run it on jsfiddle.
Code
function indexesAround(target,array) {
var start;
var len = array.length;
for(i = 0; i < len; i++) {
if (array[i] == target && !start) { start = i; }
if (array[i] > target) {
if(i == 0) { return [ 0, 0 ]; } // Target lower than array range
if(!start) { return [ i-1, i ]; } // Target inside array range but not found
return [ start, i-1 ]; // Target found
}
}
if(start) { return [ len-1, len-1 ]; } // Target higher than array range
return [ start, len-1 ]; // Target found and extends until end of array
}

Related

JS for loop in for loop, problem with scope I guess

The input is an array ints [11, 2, 7, 8, 4, 6] and and integer s 10. The function is to output an array with a pair of two numbers from ints which first form a sum of 10. So here the output should be [2, 8], because 2 + 8 = 10. Why does it output empty array? The arrResults was updated in the nested for loop, so why doesn't it show up like that after the final return statement?
function sumPairs(ints, s) {
let arrResults = [];
let sumOfTwo;
for (i = 0; i < ints.length; i++) {
for (j = 0; j < ints.length; j++) {
sumOfTwo = ints[i] + ints[j];
if (sumOfTwo === s) {
arrResults.push(ints[i]);
arrResults.push(ints[j]);
break;
}
}
if (arrResults !== []) {
break;
}
}
return arrResults;
}
console.log(sumPairs([11, 2, 7, 8, 4, 6], 10));
Beside the wrong comparing of an array with another array (without having the same object reference)
a = []
b = []
a === b // false
// other example
a = []
b = a
a === b // true
for checking the length,
a = []
a.length // 0
and by using a nearly quadratic time complexity of n², even with looping
i = 0; i < array.length - 1
j = i + 1; j < array.length
which is more then the half of n², but strill quadratic,
you could take a single loop with an object fo already seen values.
This approach finds the first pair of the array for a certain sum.
function sumPairs(ints, s) {
const needed = {};
for (const value of ints) {
if (needed[value]) return [s - value, value];
needed[s - value] = true;
}
}
console.log(sumPairs([11, 2, 7, 8, 4, 6], 10));
Your code fails because you are checking to see if the array is empty. The problem is that check is never going to be false, so it exits on the first iteration.
console.log([]===[]);
console.log([]!==[]);
So code with changes to improve performance and to exit out
function sumPairs(ints, s) {
let arrResults = [];
let sumOfTwo;
for (let i = 0; i < ints.length; i++) {
for (let j = i + 1; j < ints.length; j++) {
sumOfTwo = ints[i] + ints[j];
if (sumOfTwo === s) {
arrResults.push(ints[i]);
arrResults.push(ints[j]);
break;
}
}
if (arrResults.length) {
break;
}
}
return arrResults;
}
console.log(sumPairs([11, 2, 7, 8, 4, 6], 10));
There is no need to break out twice, just return the array
function sumPairs(ints, s) {
let arrResults = [];
let sumOfTwo;
for (let i = 0; i < ints.length; i++) {
for (let j = i + 1; j < ints.length; j++) {
sumOfTwo = ints[i] + ints[j];
if (sumOfTwo === s) {
return [ints[i], ints[j]];
}
}
}
return null;
}
console.log(sumPairs([11, 2, 7, 8, 4, 6], 10));

Given an array with numbers from 1 to a.length, how do i find the first duplicate number for which the second occurrence has the minimal index?

I'm trying the following code but it cant seem to work.
These are my tests:
input = 2,1,3,5,3,2expected output = 3;
input = 2,4,3,5,1expected output = -1
input = 2,4,3,5,1,7
Here is the code
function FirstDuplicate(array) {
var a = [5, 2, 3, 4, 2, 6, 7, 1, 2, 3];
var firstDuplicate = "";
for (var i = 0; i < a.length; i++) {
for (var b = i + 1; b < a.length; b++) {
if (a[i] === a[b])
firstDuplicate = a.indexOf(a[i]);
break;
}
}
return firstDuplicate;
}
You can create a an empty Set and keep adding the elements which are already passed to that Set. If a number comes which is already in Set then return it
function FirstDuplicate(array) {
let passed = new Set();
for(let x of array){
if(passed.has(x)) return x;
passed.add(x);
}
return -1;
}
console.log(FirstDuplicate([2,1,3,5,3,2]))
console.log(FirstDuplicate([2,4,3,5,1]))
console.log(FirstDuplicate([2,4,3,5,1,7]))
You could take an object with the value as key and check if the value has been seen before or not.
function getFirstDuplicate(array) {
var seen = Object.create(null),
i = 0,
value;
for (i = 0; i < array.length; i++) {
value = array[i];
if (seen[value]) return value;
seen[value] = true;
}
return -1;
}
console.log(getFirstDuplicate([1, 7, 3, 5, 4, 2, 9, 3]));
console.log(getFirstDuplicate([1, 7, 3, 5, 4, 2, 9, 6]));
We just push stuff into an object while we iterate over the array. On first occurrence we set it to true, if we find a true we know we found the first duplicate.
function f (arr) {
let tmp = {};
return (arr.find(v => (tmp[v] || (tmp[v] = true) && false)) || -1)
}
console.log(f([2,1,3,5,3,2]))
console.log(f([2,4,3,5,1]))
console.log(f([2,4,3,5,1,7]))

Why my Move Zeroes function is wrong answer in leetcode?

I was doing Move Zeroes in leetcode.
I write a function to solve but leetcode said it's a wrong answer.
Could someone see what is wrong in my code?
Requirement:the original array must be mutated
Input:[0,1,0,3,12]
Output:[1,3,12,0,0]
Input:[2,1]
Output:[1,2]
Here is my JS:
var moveZeroes = function(nums) {
var ZeroArray=[]
for(let i=0;i<nums.length;i++){
if(nums[i]===0){
ZeroArray.push(nums[i])
nums.splice(i,1);
}
}
nums.sort((a,b)=>(a-b))
for(let j=0;j<ZeroArray.length;j++){
nums.push(ZeroArray[j])
}
return nums;
};
console.log(moveZeroes([0,1,0,3,12])); //Should return [ 1,3,12,0,0]
console.log(moveZeroes([2,1]));//Should return [1,2]
Your
nums.shift(nums[i]);
will remove (and discard) whatever exists at the 0th index in nums at the time. It would probably be easier to push to a different array if the num is not 0, then combine the arrays at the end (no sorting):
var moveZeroes = function(nums) {
var ZeroArray = []
var nonZeroArray = [];
for (let i = 0; i < nums.length; i++) {
if (nums[i] === 0) {
ZeroArray.push(nums[i])
} else {
nonZeroArray.push(nums[i]);
}
}
return [...nonZeroArray, ...ZeroArray];
};
console.log(moveZeroes([0, 1, 0, 3, 12])) //Should return [ 1,3,12,0,0]
Or, if you do want to .sort, .sort only:
var moveZeroes = function(nums) {
nums.sort((a, b) => (a === 0) - (b === 0));
return nums;
};
console.log(moveZeroes([0, 1, 0, 3, 12])) //Should return [ 1,3,12,0,0]
Do check === 0 - otherwise, your
.sort((a,b)=>(a-b))
will put negative numbers after the 0s, when you want 0s to come at the end regardless.
If you also need to preserve the original order of non-zeros, iterate over the array, splice out 0s while keeping track of how many you remove, then push them at the end:
var moveZeroes = function(nums) {
let count = 0;
for (let i = nums.length - 1; i >= 0; i--) {
if (nums[i] === 0) {
nums.splice(i, 1);
count++;
}
}
nums.push(...new Array(count).fill(0));
return nums;
};
console.log(moveZeroes([0, 1, 0, 3, 12])) //Should return [ 1,3,12,0,0]
Array.shift() removes the first element from an array, you most likely want to use Array.splice(i, 1)
Use filter to filter all the non zero elements and then append remaining array size with zeros.
let arr = [0,1,0,3,12];
let filtered = arr.filter(item => item !== 0);
let result = [...filtered, ...(new Array(arr.length - filtered.length).fill(0))]
console.log(result);
Using splice remove zero and count the spiced values. In a loop push the zeroes in the array equal to the count
var a=[0,1,0,3,12];
var count=0;
a.forEach(e=>{
if(e==0){
a.splice(a.indexOf(e),1)
count++
}})
for(var i=0;i<count;i++)
a.push(0)
console.log(a)
You could take a single loop approach with an additonal variable for the next index for swapping the values.
This approach works in situ, as the specs requires.
/**
* #param {number[]} nums
* #return {void} Do not return anything, modify nums in-place instead.
*/
var moveZeroes = function(nums) {
var i, j = 0;
for (i = 0; i < nums.length; i++) {
console.log(...nums); // look!
if (nums[i] !== 0) { // check
[nums[j], nums[i]] = [nums[i], nums[j]]; // swap items
j++; // increment target
}
}
},
array = [0, 1, 0, 3, 12];
moveZeroes(array)
console.log(...array); // [1, 3, 12, 0, 0]
here is code to remove zeros in c++
void pushZerosToEnd(int arr[], int n)
{
int count = 0;
for (int i = 0; i < n; i++)
if (arr[i] != 0)
arr[count++] = arr[i];
while (count < n)
arr[count++] = 0;
}
int main()
{
int arr[] = {1, 9, 8, 4, 0, 0, 2, 7, 0, 6, 0, 9};
int n = sizeof(arr) / sizeof(arr[0]);
pushZerosToEnd(arr, n);
cout << "Array after pushing all zeros to end of array :n";
for (int i = 0; i < n; i++)
cout << arr[i] << " ";
return 0;
}
var a = [1, 2, 0, 0, 3, 0, 3, 0, 2, 0, 0, 0, 5, 0];
function moveZero(b) {
for (var i = b.length - 1; i >= 0; i--) {
if (b[i] === 0) {
b.splice(i, 1);
b.push(0);
}
}
return b;
}
console.log(moveZero(a));

How to get common value from 4 different array with javascript

I have below arrow and I want to get common value from all four array. I have try below code and it's working but not the correct way I want. Instead of coming [2, 3] in new array it showing other value which are common at least in two or three array.
Fiddle Demo
My Code
var a = [11, 2, 3, 4],
b = [2, 6, 3, 5],
c = [4, 2, 20, 3],
d = [34, 2, 21, 5, 3],
result = [],
common = [];
function findCommon () {
for(var i = 0; i < arguments.length; i++){
var garr = arguments[i];
result = result.concat(arguments[i]);
};
}
findCommon(a,b,c,d);
var sorted_arr = result.sort();
for(var i = 0; i< result.length-1; i++){
if(result[i+1] == sorted_arr[i]){
common.push(result[i]);
}
};
alert(common); //[2, 2, 2, 3, 3, 4, 5]
You could use arrays as the values of an object, and use the numbers as the keys. It makes it easy to count the numbers then. Note, this code is also future proof, so that if you want fewer or more arrays to test, this will let you. It also de-dupes the individual arrays, so numbers within each are only counted once to prevent errors.
function findCommon() {
var obj = {};
var out = [];
var result = [];
// convert arguments to a proper array
var args = [].slice.call(arguments);
var len = args.length;
for (var i = 0, l = len; i < l; i++) {
// grab a de-duped array and and concatenate it
// http://stackoverflow.com/a/9229821/1377002
var unique = args[i].filter(function(item, pos) {
return args[i].indexOf(item) == pos;
});
result = result.concat(unique);
}
for (var i = 0, l = result.length; i < l; i++) {
var el = result[i];
// if the object key doesn't exist, create an array
// as the value; add the number to the array
if (!obj[el]) obj[el] = [];
obj[el].push(el);
}
for (var p in obj) {
// if the array length equals the original length of
// the number of arrays added to the function
// add it to the output array, as an integer
if (obj[p].length === len) out.push(+p);
}
return out;
}
findCommon(a, b, c, d); // [2]
In addition, this will find all multiple keys, so if you replace the 5 in d as 3, the result will be [2, 3].
DEMO which uses 4 arrays, multiple hits
DEMO which uses 5 arrays
Try this:
function findCommon()
{
var common=[];
for(i=0; i<a.length; i++)
{
if(b.indexOf(i) != -1 && c.indexOf(i) != -1 && d.indexOf(i) != -1)
{
common.push(i);
}
}
return common;
}
This will return array of common values between all four arrays. Here is the working fiddle.
Assuming you want something generic for X arrays and we are talking about integers this sounds to me like some bucket business.
var a = [11, 2, 3, 4],
b = [2, 6, 3, 5],
c = [4, 2, 20, 3],
d = [34, 2, 21, 5];
function findCommon()
{
var bucket = [];
var maxVal = 0;
var minVal = 0;
for(var i = 0; i < arguments.length; i++)
{
for(var j = 0 ; j < arguments[i].length ; j++)
{
var val = arguments[i][j];
bucket[val] = bucket[val] + 1 || 1;
if(val > maxVal)
{
maxVal = val;
}
else if(val < minVal)
{
minVal = val;
}
}
}
var retVal = 0;
var maxTimes = 0;
for(var i = minVal ; i <= maxVal ; i++)
{
var val = bucket[i];
if(val > maxTimes)
{
maxTimes = val;
retVal = i;
}
}
return retVal;
}
console.log(findCommon(a,b,c,d));
JSFIDDLE.
You cna use indexOf to resolve that:
array.indexOf(element) >=0 // it means that element is in array
or
array.indexOf(element) != -1
When you would be using jQuery, thre is a shorter version:
$.inArray(value, array)
More fancy way, would be to use filter (Filter):
function hasElement(element, index, array) {
return element.indexOf(i) >= 0;
}
filtered = [a,b,c,d].filter(hasElement);
filtered.length === 4
This Try,
will find the common number with number of times it repeated.
var a = [11, 2, 3, 4],
b = [2, 6, 3, 5],
c = [4, 2, 20, 3],
d = [34, 2, 21, 5],
result = [],
common = [];
var all = a.concat(b).concat(c).concat(d);
var commonNum=0;
var largestCounter=0
for(var i = 0; i< all.length-1; i++){
var counter=0;
if(a.indexOf(all[i])>-1) counter+=1;
if(b.indexOf(all[i])>-1) counter+=1;
if(c.indexOf(all[i])>-1) counter+=1;
if(d.indexOf(all[i])>-1) counter+=1;
if(counter>largestCounter)
{largestCounter = counter;
commonNum = all[i];
}
};
alert(commonNum+" with count " + largestCounter);

JavaScript quicksort

I have been looking around the web for a while and I am wondering if there is a 'stable' defacto implementation of quicksort that is generally used? I can write my own but why reinvent the wheel...
Quicksort (recursive)
function quicksort(array) {
if (array.length <= 1) {
return array;
}
var pivot = array[0];
var left = [];
var right = [];
for (var i = 1; i < array.length; i++) {
array[i] < pivot ? left.push(array[i]) : right.push(array[i]);
}
return quicksort(left).concat(pivot, quicksort(right));
};
var unsorted = [23, 45, 16, 37, 3, 99, 22];
var sorted = quicksort(unsorted);
console.log('Sorted array', sorted);
You can easily "stabilize" an unstable sort using a decorate-sort-undecorate pattern
function stableSort(v, f)
{
if (f === undefined) {
f = function(a, b) {
a = ""+a; b = ""+b;
return a < b ? -1 : (a > b ? 1 : 0);
}
}
var dv = [];
for (var i=0; i<v.length; i++) {
dv[i] = [v[i], i];
}
dv.sort(function(a, b){
return f(a[0], b[0]) || (a[1] - b[1]);
});
for (var i=0; i<v.length; i++) {
v[i] = dv[i][0];
}
}
the idea is to add the index as last sorting term so that no two elements are now "the same" and if everything else is the same the original index will be the discriminating factor.
Put your objects into an array.
Call Array.sort(). It's very fast.
var array = [3,7,2,8,2,782,7,29,1,3,0,34];
array.sort();
console.log(array); // prints [0, 1, 2, 2, 29, 3, 3, 34, 7, 7, 782, 8]
Why does that print in lexicographic order? That's how array.sort() works by default, e.g. if you don't provide a comparator function. Let's fix this.
var array = [3,7,2,8,2,782,7,29,1,3,0,34];
array.sort(function (a, b)
{
return a-b;
});
console.log(array); // prints [0, 1, 2, 2, 3, 3, 7, 7, 8, 29, 34, 782]
Quick Sort (ES6)
function quickSort(arr) {
if (arr.length < 2) {
return arr;
}
const pivot = arr[Math.floor(Math.random() * arr.length)];
let left = [];
let right = [];
let equal = [];
for (let val of arr) {
if (val < pivot) {
left.push(val);
} else if (val > pivot) {
right.push(val);
} else {
equal.push(val);
}
}
return [
...quickSort(left),
...equal,
...quickSort(right)
];
}
Few notes:
A random pivot keeps the algorithm efficient even when the data is sorted.
As much as it nice to use Array.filter instead of using for of loop, like some of the answers here, it will increase time complexity (Array.reduce can be used instead though).
A Functional equivalent
In celebration of Functional Javascript, which appears to be the in thing
at the moment, especially given ES6+ wonderful syntactic sugar additions. Arrow functions and destructuring I propose a very clean, short functional equivalent of the quicksort function. I have not tested it for performance or compared it to the built-in quicksort function but it might help those who are struggling to understand the practical use of a quicksort. Given its declarative nature it is very easy to see what is happening as oppose to how it works.
Here is a JSBin version without comments https://jsbin.com/zenajud/edit?js,console
function quickSortF(arr) {
// Base case
if (!arr.length) return []
// This is a ES6 addition, it uses destructuring to pull out the
// first value and the rest, similar to how other functional languages
// such as Haskell, Scala do it. You can then use the variables as
// normal below
const [head, ...tail] = arr,
// here we are using the arrow functions, and taking full
// advantage of the concise syntax, the verbose version of
// function(e) => { return e < head } is the same thing
// so we end up with the partition part, two arrays,
// one smaller than the pivot and one bigger than the
// pivot, in this case is the head variable
left = tail.filter( e => e < head),
right = tail.filter( e => e >= head)
// this is the conquer bit of divide-and-conquer
// recursively run through each left and right array
// until we hit the if condition which returns an empty
// array. These results are all connected using concat,
// and we get our sorted array.
return quickSortF(left).concat(head, quickSortF(right))
}
const q7 = quickSortF([11,8,14,3,6,2,7])
//[2, 3, 6, 7, 8, 11, 14]
const q8 = quickSortF([11,8,14,3,6,2,1, 7])
//[1, 2, 3, 6, 7, 8, 11, 14]
const q9 = quickSortF([16,11,9,7,6,5,3, 2])
//[2, 3, 5, 6, 7, 9, 11, 16]
console.log(q7,q8,q9)
The comments should provide enough if it is already not clear what is happening. The actual code is very short without comments, and you may have noticed I am not a fan of the semicolon. :)
In this blog http://www.nczonline.net/blog/2012/11/27/computer-science-in-javascript-quicksort/ which has pointed out that
Array.sort is implemented in quicksort or merge sort internaly.
Quicksort is generally considered to be efficient and fast and so is
used by V8 as the implementation for Array.prototype.sort() on arrays
with more than 23 items. For less than 23 items, V8 uses insertion
sort[2]. Merge sort is a competitor of quicksort as it is also
efficient and fast but has the added benefit of being stable. This is
why Mozilla and Safari use it for their implementation of
Array.prototype.sort().
and when using Array.sort,you should return -1 0 1 instead of true or false in Chrome.
arr.sort(function(a,b){
return a<b;
});
// maybe--> [21, 0, 3, 11, 4, 5, 6, 7, 8, 9, 10, 1, 2, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22]
arr.sort(function(a,b){
return a > b ? -1 : a < b ? 1 : 0;
});
// --> [22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
var array = [8, 2, 5, 7, 4, 3, 12, 6, 19, 11, 10, 13, 9];
quickSort(array, 0, array.length -1);
document.write(array);
function quickSort(arr, left, right)
{
var i = left;
var j = right;
var tmp;
pivotidx = (left + right) / 2;
var pivot = parseInt(arr[pivotidx.toFixed()]);
/* partition */
while (i <= j)
{
while (parseInt(arr[i]) < pivot)
i++;
while (parseInt(arr[j]) > pivot)
j--;
if (i <= j)
{
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
i++;
j--;
}
}
/* recursion */
if (left < j)
quickSort(arr, left, j);
if (i < right)
quickSort(arr, i, right);
return arr;
}
Using ES6 rest, spread:
smaller = (a, list) => list.filter(x => x <= a)
larger = (a, list) => list.filter(x => x > a)
qsort = ([x, ...list]) => (!isNaN(x))
? [...qsort(smaller(x, list)), x, ...qsort(larger(x, list))]
: []
This algorithm work almost as fast as the default implementation
of Array.prototype.sort in chrome.
function quickSort(t){
_quickSort(t,0,t.length-1,0,t.length-1);
}
function _quickSort(t, s, e, sp, ep){
if( s>=e ) return;
while( sp<ep && t[sp]<t[e] ) sp++;
if( sp==e )
_quickSort(t,s,e-1,s,e-1);
else{
while(t[ep]>=t[e] && sp<ep ) ep--;
if( sp==ep ){
var temp = t[sp];
t[sp] = t[e];
t[e] = temp;
if( s!=sp ){
_quickSort(t,s,sp-1,s,sp-1);
}
_quickSort(t,sp+1,e,sp+1,e);
}else{
var temp = t[sp];
t[sp] = t[ep];
t[ep] = temp;
_quickSort(t,s,e,sp+1,ep);
}
}
}
quickSort time (ms): 738
javaScriptSort time (ms): 603
var m = randTxT(5000,500,-1000,1000);
VS(m);
function VS(M){
var t;
t = Date.now();
for(var i=0;i<M.length;i++){
quickSort(M[i].slice());
}console.log("quickSort time (ms): "+(Date.now()-t));
t = Date.now();
for(var i=0;i<M.length;i++){
M[i].slice().sort(compare);
}console.log("javaScriptSort time (ms): "+(Date.now()-t));
}
function compare(a, b) {
if( a<b )
return -1;
if( a==b )
return 0;
return 1;
}
function randT(n,min,max){
var res = [], i=0;
while( i<n ){
res.push( Math.floor(Math.random()*(max-min+1)+min) );
i++;
}
return res;
}
function randTxT(n,m,min,max){
var res = [], i=0;
while( i<n ){
res.push( randT(m,min,max) );
i++;
}
return res;
}
Yet another quick sort demonstration, which takes middle of the array as pivot for no specific reason.
const QuickSort = function (A, start, end) {
//
if (start >= end) {
return;
}
// return index of the pivot
var pIndex = Partition(A, start, end);
// partition left side
QuickSort(A, start, pIndex - 1);
// partition right side
QuickSort(A, pIndex + 1, end);
}
const Partition = function (A, start, end) {
if (A.length > 1 == false) {
return 0;
}
let pivotIndex = Math.ceil((start + end) / 2);
let pivotValue = A[pivotIndex];
for (var i = 0; i < A.length; i++) {
var leftValue = A[i];
//
if (i < pivotIndex) {
if (leftValue > pivotValue) {
A[pivotIndex] = leftValue;
A[i] = pivotValue;
pivotIndex = i;
}
}
else if (i > pivotIndex) {
if (leftValue < pivotValue) {
A[pivotIndex] = leftValue;
A[i] = pivotValue;
pivotIndex = i;
}
}
}
return pivotIndex;
}
const QuickSortTest = function () {
const arrTest = [3, 5, 6, 22, 7, 1, 8, 9];
QuickSort(arrTest, 0, arrTest.length - 1);
console.log("arrTest", arrTest);
}
//
QuickSortTest();
I really thought about this question. So first I found the normal search mode and wrote.
let QuickSort = (arr, low, high) => {
if (low < high) {
p = Partition(arr, low, high);
QuickSort(arr, low, p - 1);
QuickSort(arr, p + 1, high);
}
return arr.A;
}
let Partition = (arr, low, high) => {
let pivot = arr.A[high];
let i = low;
for (let j = low; j <= high; j++) {
if (arr.A[j] < pivot) {
[arr.A[i], arr.A[j]] = [arr.A[j], arr.A[i]];
i++;
}
}
[arr.A[i], arr.A[high]] = [arr.A[high], arr.A[i]];
return i;
}
let arr = { A/* POINTER */: [33, 22, 88, 23, 45, 0, 44, 11] };
let res = QuickSort(arr, 0, arr.A.length - 1);
console.log(res);
Result is [0, 11, 22, 23, 33, 44, 45, 88]
But its not stable; so I checked the other answers and the Idea of #6502 was interesting to me that "two items do not have to be the same" to be distinguishable.
Well, I have a solution in my mind, but it is not optimal. We can keep the indexes of the items in a separate array. Memory consumption will almost double in this idea.
arr.A => Array of numbers
arr.I => Indexes related to each item of A
influencer => This should be a very very small number; I want to use this as a factor to be able to distinguish between similar items.
So we can change the partition like this:
let Partition = (arr, low, high) => {
let pivot = arr.A[high];
let index = arr.I[high];
let i = low;
for (let j = low; j <= high; j++) {
if (arr.A[j] + (arr.I[j] * influencer) < pivot + (index * influencer)) {
[arr.A[i], arr.A[j]] = [arr.A[j], arr.A[i]];
[arr.I[i], arr.I[j]] = [arr.I[j], arr.I[i]];
i++;
}
}
[arr.A[i], arr.A[high]] = [arr.A[high], arr.A[i]];
[arr.I[i], arr.I[high]] = [arr.I[high], arr.I[i]];
return i;
}
let influencer = 0.0000001;
let arr = {
I/* INDEXES */: [10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
A/* POINTER */: [33, 22, 88, 33, 23, 45, 33, 89, 44, 11]
};
let res = QuickSort(arr, 0, arr.A.length - 1);
console.log(res);
Result:
I: [19, 11, 14, 10, 13, 16, 18, 15, 12, 17],
A: [11, 22, 23, 33, 33, 33, 44, 45, 88, 89]
More compact and easy to understand quicksort implementation
const quicksort = arr =>
arr.length <= 1
? arr
: [
...quicksort(arr.slice(1).filter((el) => el < arr[0])),
arr[0],
...quicksort(arr.slice(1).filter((el) => el >= arr[0])),
];
try my solution
const quickSort = (arr) => {
// base case
if(arr.length < 2) return arr;
// recurisve case
// pick a random pivot
let pivotIndex = Math.floor(Math.random() * arr.length);
let pivot = arr[pivotIndex];
let left = [];
let right = [];
// make array of the elements less than pivot and greater than it
for(let i = 0; i < arr.length; i++) {
if(i === pivotIndex) {
continue;
}
if(arr[i] < pivot) {
left.push(arr[i])
} else {
right.push(arr[i])
}
}
// call the recursive case again
return quickSort(left).concat([pivot], quickSort(right));
}
when testing this
quickSort([7, 5, 3, 2, 8, 1, 5]) // returns [[1, 2, 3, 5, 5, 7, 8]]
This is it !!!
function typeCheck(a, b){
if(typeof a === typeof b){
return true;
}else{
return false;
}
}
function qSort(arr){
if(arr.length === 0){
return [];
}
var leftArr = [];
var rightArr = [];
var pivot = arr[0];
for(var i = 1; i < arr.length; i++){
if(typeCheck(arr[i], parseInt(0))){
if(arr[i] < pivot){
leftArr.push(arr[i]);
}else { rightArr.push(arr[i]) }
}else{
throw new Error("All must be integers");
}
}
return qSort(leftArr).concat(pivot, qSort(rightArr));
}
var test = [];
for(var i = 0; i < 10; i++){
test[i] = Math.floor(Math.random() * 100 + 2);
}
console.log(test);
console.log(qSort(test));
Slim version:
function swap(arr,a,b){
let temp = arr[a]
arr[a] = arr[b]
arr[b] = temp
return 1
}
function qS(arr, first, last){
if(first > last) return
let p = first
for(let i = p; i < last; i++)
if(arr[i] < arr[last])
p += swap(arr, i, p)
swap(arr, p, last)
qS(arr, first, p - 1)
qS(arr, p + 1, last)
}
Tested with random values Arrays, and seems to be always faster than Array.sort()
quickSort = (array, left, right) => {
if (left >= right) {
return;
}
const pivot = array[Math.trunc((left + right) / 2)];
const index = partition(array, left, right, pivot);
quickSort(array, left, index - 1);
quickSort(array, index, right);
}
partition = (array, left, right, pivot) => {
while (left <= right) {
while (array[left] < pivot) {
left++;
}
while (array[right] > pivot) {
right--;
}
if (left <= right) {
swap(array, left, right);
left++;
right--;
}
}
return left;
}
swap = (array, left, right) => {
let temp = array[left];
array[left] = array[right];
array[right] = temp;
}
let array = [1, 5, 2, 3, 5, 766, 64, 7678, 21, 567];
quickSort(array, 0, array.length - 1);
console.log('final Array: ', array);
A Fastest implementation
const quickSort = array =>
(function qsort(arr, start, end) {
if (start >= end) return arr;
let swapPos = start;
for (let i = start; i <= end; i++) {
if (arr[i] <= arr[end]) {
[arr[swapPos], arr[i]] = [arr[i], arr[swapPos]];
swapPos++;
}
}
qsort(arr, start, --swapPos - 1);
qsort(arr, swapPos + 1, end);
return arr;
})(Array.from(array), 0, array.length - 1);
Quicksort using ES6, filter and spread operation.
We establish a base case that 0 or 1 elements in an array are already sorted. Then we establish an inductive case that if quicksort works for 0 or 1 elements, it can work for an array of size 2. We then divide and conquer until and recursively call our function until we reach our base case in the call stack to get our desired result.
O(n log n)
const quick_sort = array => {
if (array.length < 2) return array; // base case: arrays with 0 or 1 elements are already "sorted"
const pivot = array[0]; // recursive case;
const slicedArr = array.slice(1);
const left = slicedArr.filter(val => val <= pivot); // sub array of all elements less than pivot
const right = slicedArr.filter(val => val > pivot); // sub array of all elements greater than pivot
return [...quick_sort(left), pivot, ...quick_sort(right)];
}
const quicksort = (arr)=>{
const length = Math.ceil(arr.length/2);
const pivot = arr[length];
let newcondition=false;
for(i=0;i<length;i++){
if(arr[i]>arr[i+1]){
[arr[i], arr[i+1]] = [arr[i+1], arr[i]]
newcondition =true
}
}
for(i=arr.length;i>length-1;i--){
if(arr[i]>arr[i+1]){
[arr[i], arr[i+1]] = [arr[i+1], arr[i]]
newcondition =true
}
}
return newcondition? quicksort(arr) :arr
}
const t1 = performance.now();
const t2 = performance.now();
console.log(t2-t1);
console.log(quicksort([3, 2, 4, 9, 1, 0, 8, 7]));
const quicksort = (arr)=>{
if (arr.length < 2) return arr;
const pivot = arr[0];
const left = [];
const right = [];
arr.shift();
arr.forEach(number => {
(number<pivot) ? left.push(number) : right.push(number);
});
return ([...quicksort(left), pivot, ...quicksort(right)]);
}
console.log(quicksort([6, 23, 29, 4, 12, 3, 0, 97]));
How about this non-mutating functional QuickSort:
const quicksort = (arr, comp, iArr = arr) => {
if (arr.length < 2) {
return arr;
}
const isInitial = arr.length === iArr.length;
const arrIndexes = isInitial ? Object.keys(arr) : arr;
const compF = typeof comp === 'function'
? comp : (left, right) => left < right ? -1 : right < left ? 1 : 0;
const [pivotIndex, ...indexesSansPivot] = arrIndexes;
const indexSortReducer = isLeftOfPivot => [
(acc, index) => isLeftOfPivot === (compF(iArr[index], iArr[pivotIndex]) === -1)
? acc.concat(index) : acc,
[]
];
const ret = quicksort(indexesSansPivot.reduce(...indexSortReducer(true)), compF, iArr)
.concat(pivotIndex)
.concat(quicksort(indexesSansPivot.reduce(...indexSortReducer(false)), compF, iArr));
return isInitial ? ret.reduce((acc, index) => acc.concat([arr[index]]), []) : ret;
};
As a bonus, it supports optional comparing function which enables sorting of array of objects per property/properties, and doesn't get slower if dealing with larger values/objects.
First quick sorts original array keys, then returns sorted copy of original array.

Categories

Resources