Is there any built-in JavaScript function to do a partial sort? If not, what is a good way to implement it?
Given an unsorted array of N elements, I would like to find K elements that are minimal with respect to some weighting function. K is much smaller than N, so it would be inefficient to sort the whole array and take the first K elements.
I would be happy even if there was something non-standard, browser-dependent. I could still fallback to the custom JavaScript implementation.
PS: This is my current custom implementation (without taking a weighting function into account, just sorting the elements as they are for simplicity):
function bisect(items, x, lo, hi) {
var mid;
if (typeof(lo) == 'undefined') lo = 0;
if (typeof(hi) == 'undefined') hi = items.length;
while (lo < hi) {
mid = Math.floor((lo + hi) / 2);
if (x < items[mid]) hi = mid;
else lo = mid + 1;
}
return lo;
}
function insort(items, x) {
items.splice(bisect(items, x), 0, x);
}
function partialSort(items, k) {
var smallest = [];
for (var i = 0, len = items.length; i < len; ++i) {
var item = items[i];
if (smallest.length < k || item < smallest[smallest.length - 1]) {
insort(smallest, item);
if (smallest.length > k)
smallest.splice(k, 1);
}
}
return smallest;
}
console.log(partialSort([5, 4, 3, 2, 1, 6, 7, 8, 1, 9], 3));
The algorithm walks through the given array one single time, keeping track of a sorted list of the k smallest items so far, using binary search to insert new elements.
Please post alternative solutions if you think they might be faster or more elegant. Timings are very welcome.
No. There's only the full array sort, so you will need to use your own implementation.
Little improvement on your code (I had thought of exactly the same algorithm :-)):
function partialSort(items, k) {
var smallest = items.slice(0, k).sort(),
max = smallest[k-1];
for (var i = k, len = items.length; i < len; ++i) {
var item = items[i];
if (item < max) {
insort(smallest, item);
smallest.length = k;
max = smallest[k-1];
}
}
return smallest;
}
(Even seems to be a little faster, I guess due to caching the max variable)
For relatively small k it can be worth it to implement a Max Heap (by lack of a native one in JavaScript):
Create a Max Heap of the first k values
For each remaining value:
If it is less than the root of the heap, replace the root with this value. Otherwise ignore the value. Note that the heap never changes in size.
Finally sort the heap and return it.
This is in fact an improvement on another idea using a Min Heap, but that one needs to heapify the whole array, and so will not run as fast. After heapifying the whole array, you just extract k times a value from that heap, and return those values.
I have added both solutions to Bergi's jsperf.com performance tests (copied to jsbench.me). For that particular test (5000 array values, k = 10), the Max Heap solution is faster. But this advantage will shrink as k is increased.
Here is the code for the Max Heap solution:
// A few Heap-functions that operate on an array
function maxSiftDown(arr, i=0, value=arr[i]) {
if (i >= arr.length) return;
while (true) {
var j = i*2+1;
if (j+1 < arr.length && arr[j] < arr[j+1]) j++;
if (j >= arr.length || value >= arr[j]) break;
arr[i] = arr[j];
i = j;
}
arr[i] = value;
}
function maxHeapify(arr) {
for (var i = arr.length>>1; i--; ) maxSiftDown(arr, i);
return arr;
}
// The main algorithm
function partialSortWithMaxHeap(items, k) {
var heap = maxHeapify(items.slice(0, k));
for (var i = k, len = items.length; i < len; ++i) {
var item = items[i];
if (item < heap[0]) maxSiftDown(heap, 0, item);
}
return heap.sort((a,b) => a-b);
}
// Sample data & call
var arr = Array.from({length:5000}, () => Math.floor(Math.random() * 1e5));
console.log(partialSortWithMaxHeap(arr, 10));
There's no native partial sort function. The closest thing to what you want is Array.filter.
function isSmallEnough(element, index, array) {
return (element <= 10);
}
var filtered = [12, 5, 8, 130, 44].filter(isSmallEnough);
// filtered is [5, 8]
The example was borrowed (and slightly modified) from the above link.
I made a version than works with objects, like Array.sort(f):
function partialSort(items, k,f) {
function bisect(items, x, lo, hi) {
var mid;
if (typeof(lo) == 'undefined') lo = 0;
if (typeof(hi) == 'undefined') hi = items.length;
while (lo < hi) {
mid = Math.floor((lo + hi) / 2);
if (0>f(x,items[mid])) hi = mid;
else lo = mid + 1;
}
return lo;
}
function insort(items, x) {
items.splice(bisect(items, x), 0, x);
}
var smallest = items.slice(0, k).sort(f),
max = smallest[k-1];
for (var i = k, len = items.length; i < len; ++i) {
var item = items[i];
if (0>f(item,max)) {
insort(smallest, item);
smallest.length = k;
max = smallest[k-1];
}
}
return smallest;
}
// [ { e: 1 }, { e: 1 }, { e: 2 } ]
console.log(partialSort([{e:4},{e:6},{e:1},{e:8},{e:3},{e:1},{e:6},{e:2}],3,(a,b)=>a.e-b.e))
console.log()
In the code block below, the nisetamafibo function keeps an array of the smallest items found so far. The array is sorted and truncated to length K after a certain number of new items have been added to the array, where the number is taken from the Fibonacci sequence so that it is first 1, next 1, then 2, then 3, then 5, and so on. The nisetamadouble method doubles the interval after which the array is sorted instead, so that it is first 1, then 2, then 4, and so on. (I also tried the approach that I sorted the array each time after a fixed number of new items like 10 had been added, but it was slower. And I also tried to initialize the array at the start of the function so that I took in a fixed number of the first items of the input and sorted them, but I found that initializing the array with 1 or 0 items was the fastest, so I removed the initialization step.)
The nisetamainsertion function uses insertion sort to sort the items. It's very slow at high K-values because insertion sort has quadratic time complexity, but it's fast at K-values of around 10 to 100 or lower, because insertion sort is fast for short arrays. The nisetamachoose method chooses nisetamainsertion for K-values of 100 or less but nisetamafibo otherwise. (In the Java JDK, the file DualPivotQuicksort.java uses insertion sort instead of quicksort for arrays with less than 47 items. A presentation about sorting algorithms in R said that "fastest for < 30 items is insert sort".)
I also tried to implement the quickselect algorithm with and without recursion. The version that didn't use recursion was a bit faster, but both versions were still slow compared to other methods especially in cases where N was high and K was low.
On another Stack Exchange site, someone came up with new variants of the Floyd-Rivest algorithm which were faster than the regular Floyd-Rivest algorithm in C: https://softwareengineering.stackexchange.com/questions/284767/kth-selection-routine-floyd-algorithm-489. I tried to implement the variant called select7MO3 in JavaScript, but it ended up being one of the slowest options in my benchmark.
function nisetamafibo(a,k=1){
let found=[],len=a.length,unsorted=0,biggestfound=Infinity,nextsort=1,prevsort=1,oldsort
for(let i=0;i<len;i++){
if(a[i]<biggestfound||i<k){
found.push(a[i])
if(++unsorted==nextsort){
found.sort((l,r)=>l<r?-1:l>r?1:0)
found=found.slice(0,k)
biggestfound=found[found.length-1]
oldsort=nextsort;nextsort+=prevsort;prevsort=oldsort
unsorted=0
}
}
}
found.sort((l,r)=>l<r?-1:l>r?1:0)
return found.slice(0,k)
}
function nisetamadouble(a,k=1){
let found=[],len=a.length,unsorted=0,biggestfound=Infinity,nextsort=1
for(let i=0;i<len;i++){
if(a[i]<biggestfound||i<k){
found.push(a[i])
if(++unsorted==nextsort){
found.sort((l,r)=>l<r?-1:l>r?1:0)
found=found.slice(0,k)
biggestfound=found[found.length-1]
nextsort*=2
unsorted=0
}
}
}
found.sort((l,r)=>l<r?-1:l>r?1:0)
return found.slice(0,k)
}
function nisetamainsertion(a,k=1){
let found=a.slice(0,k),l=a.length
found.sort((l,r)=>l<r?-1:l>r?1:0)
let biggestfound=found[k-1]
for(let i=0;i<l;i++){
let v=a[i]
if(v<biggestfound){
let insertat=k-1
for(let j=0;j<k-1;j++)if(v<found[j]||j==i){insertat=j;break}
for(let j=k-1;j>insertat;j--)found[j]=found[j-1]
found[insertat]=v
biggestfound=found[k-1]
}
}
return found
}
function nisetamachoose(a,k=1){
return k<=100?nisetamainsertion(a,k):nisetamafibo(a,k)
}
function quickselect(a,k,l,r){
l=l||0
r=r||a.length-1
while(true){
let pivot=a[r],pos=l
for(let i=l;i<=r;i++)if(a[i]<pivot){let temp=a[i];a[i]=a[pos];a[pos++]=temp}
let temp=a[r];a[r]=a[pos];a[pos]=temp
if(pos==k)break
pos<k?l=pos+1:r=pos-1
}
}
function quickselectrecursive(a,k,l,r){
l=l||0
r=r||a.length-1
let pivot=a[r],pos=l
for(let i=l;i<=r;i++)if(a[i]<pivot){let temp=a[i];a[i]=a[pos];a[pos++]=temp}
let temp=a[r];a[r]=a[pos];a[pos]=temp
if(pos<k)quickselectrecursive(a,pos+1,r,k)
if(pos>k)quickselectrecursive(a,l,pos-1,k)
}
function sortslice(a,k){
a.sort((l,r)=>l<r?-1:l>r?1:0)
return a.slice(0,k)
}
// https://softwareengineering.stackexchange.com/questions/284767/kth-selection-routine-floyd-algorithm-489
function select7MO3(a,k){
let l=0,i,r=a.length-1,rr=r,ll=l
while(r>l){
if(a[k]<a[l]){let t=a[l];a[l]=a[k];a[k]=t}
if(a[r]<a[l]){let t=a[l];a[l]=a[r];a[r]=t}
if(a[r]<a[k]){let t=a[k];a[k]=a[r];a[r]=t}
if((r-l)>k){
let n=r-l+1
i=k-l+1
let s=(2*n/3)
let div=i-n
let sd=(n*s*(n-s)/n)*(div<0?-1:div>0?1:0)
ll=Math.max(l,k-i*s/n+sd)
rr=Math.min(r,k+(n-i)*s/n+sd)
}
let pivot=a[k]
i=l
let j=r
let t=a[l];a[l]=a[k];a[k]=t
if(a[r]>pivot){t=a[r];a[r]=a[l];a[l]=t}
while(i<j){
let t=a[i];a[i]=a[j];a[j]=t
i++
j--
while(a[i]<pivot)i++
while(a[j]>pivot)j--
}
if(a[l]==pivot){i--;let t=a[l];a[l]=a[j];a[j]=t}
else{j++;let t=a[j];a[j]=a[r];a[r]=t}
if(j<=k)l=j+1
else if(k<=j)r=j-1
}
let out=a.slice(0,k)
out.sort((l,r)=>l<r?-1:l>r?1:0)
return out
}
// OP and Bergi
function bisect(items, x, lo, hi) {
var mid;
if (typeof(lo) == 'undefined') lo = 0;
if (typeof(hi) == 'undefined') hi = items.length;
while (lo < hi) {
mid = Math.floor((lo + hi) / 2);
if (x < items[mid]) hi = mid;
else lo = mid + 1;
}
return lo;
}
function insort(items, x) {
items.splice(bisect(items, x), 0, x);
}
function OP(items, k) {
var smallest = [];
for (var i = 0, len = items.length; i < len; ++i) {
var item = items[i];
if (smallest.length < k || item < smallest[smallest.length - 1]) {
insort(smallest, item);
if (smallest.length > k)
smallest.splice(k, 1);
}
}
return smallest;
}
function OP_Bergi(items, k) {
var smallest = items.slice(0, k).sort(),
max = smallest[k-1];
for (var i = k, len = items.length; i < len; ++i) {
var item = items[i];
if (item < max) {
insort(smallest, item);
smallest.length = k;
max = smallest[k-1];
}
}
return smallest;
}
// trincot
function maxSiftDown(arr, i=0, value=arr[i]) {
if (i >= arr.length) return;
while (true) {
var j = i*2+1;
if (j+1 < arr.length && arr[j] < arr[j+1]) j++;
if (j >= arr.length || value >= arr[j]) break;
arr[i] = arr[j];
i = j;
}
arr[i] = value;
}
function maxHeapify(arr) {
for (var i = arr.length>>1; i--; ) maxSiftDown(arr, i);
return arr;
}
function trincot_MaxHeap(items, k) {
var heap = maxHeapify(items.slice(0, k));
for (var i = k, len = items.length; i < len; ++i) {
var item = items[i];
if (item < heap[0]) maxSiftDown(heap, 0, item);
}
return heap.sort((a,b) => a-b);
}
// DiazJara
function DiazJara(items, k,f) {
function bisect(items, x, lo, hi) {
var mid;
if (typeof(lo) == 'undefined') lo = 0;
if (typeof(hi) == 'undefined') hi = items.length;
while (lo < hi) {
mid = Math.floor((lo + hi) / 2);
if (0>f(x,items[mid])) hi = mid;
else lo = mid + 1;
}
return lo;
}
function insort(items, x) {
items.splice(bisect(items, x), 0, x);
}
var smallest = items.slice(0, k).sort(f),
max = smallest[k-1];
for (var i = k, len = items.length; i < len; ++i) {
var item = items[i];
if (0>f(item,max)) {
insort(smallest, item);
smallest.length = k;
max = smallest[k-1];
}
}
return smallest;
}
// benchmark
for(let nk of'31 33 40 42 44 51 53 55 60 62 64 66 71 73 75'.split(' ')){
let n=parseInt(nk[0]),k0=parseInt(nk[1]),k=10**k0
let opt=[
'OP(a,k)',
'OP_Bergi(a,k)',
'trincot_MaxHeap(a,k)',
'DiazJara(a,k,(l,r)=>l-r)',
'DiazJara(a,k,(l,r)=>l<r?-1:l>r?1:0)',
'nisetamafibo(a,k)',
'nisetamadouble(a,k)',
// 'nisetamainsertion(a,k)', // this would've taken too long to run at K=1e6
'nisetamachoose(a,k)',
'quickselect(a,k);a=a.slice(0,k);a.sort((l,r)=>l<r?-1:l>r?1:0)',
'quickselectrecursive(a,k);a=a.slice(0,k);a.sort((l,r)=>l<r?-1:l>r?1:0)',
'select7MO3(a,k);a=a.slice(0,k);a.sort((l,r)=>l<r?-1:l>r?1:0)',
'sortslice(a,k)'
]
let ord=Array.from({length:100},()=>Array(opt.length).fill().map((_,i)=>i)).flat()
ord.sort(()=>Math.random()-.5)
for(let x of ord){
let o=opt[x]
let a=Array.from({length:10**n},()=>Math.random())
let t1=process.hrtime.bigint()
eval(o)
let t2=process.hrtime.bigint()-t1
console.log(n+' '+k0+' '+o+' '+t2)
}
}
This shows the median time of a hundred runs in ms and the average rank of each method (where for example 7/4 means that N was 1e7 and K was 1e4):
For most combinations of N and K, Bergi's modified version of the OP's code was actually slower than the OP's code, even though the OP's code was extremely slow in the case where N and K were both 1e6.
(l,r)=>l<r?-1:l>r?1:0 is faster than (l,r)=>l-r as you can see by comparing the two versions of Díaz-Jara's method above.
Here's also versions of my nisetamadouble and nisetamainsertion methods which return the indexes of the smallest items in addition to the values:
let a=Array.from({length:1e5},()=>Math.random())
let k=10
let l=a.length
let biggestfound=Infinity,foundind=[],foundval=[]
for(let i=0;i<l;i++){
let v=a[i]
if(i<k||v<biggestfound){
let insertat=k-1
for(let j=0;j<k-1;j++)if(v<foundval[j]||j==i){insertat=j;break}
for(let j=k-1;j>insertat;j--){foundind[j]=foundind[j-1];foundval[j]=foundval[j-1]}
foundind[insertat]=i
foundval[insertat]=v
biggestfound=foundval[k-1]
}
}
console.log(foundind)
console.log(foundval)
function nisetama(a,k=1){
let found=[],len=a.length,unsorted=0,biggestfound=Infinity,nextsort=1
for(let i=0;i<len;i++){
if(a[i]<biggestfound||i<k){
found.push(a[i])
if(++unsorted==nextsort){
found.sort((l,r)=>l<r?-1:l>r?1:0)
found=found.slice(0,k)
biggestfound=found[found.length-1]
nextsort*=2
unsorted=0
}
}
}
found.sort((l,r)=>l<r?-1:l>r?1:0)
return found.slice(0,k)
}
let a2=a
nisetama(a2,k)
biggestfound=a2[k-1],foundind=[]
for(let i=0;i<l;i++)if(a[i]<=biggestfound)foundind.push(i)
foundind.sort((l,r)=>a[l]<a[r]?-1:a[l]>a[r]?1:0)
foundind=foundind.slice(0,k)
console.log(foundind)
console.log(foundval)
Related
I'm trying to solve a beginner problem on leetcode.
Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target.
My solution works well on some of the testcases, but fail on this:
*
Example:
Input: nums = [3,3], target = 6
Output: [0,1]
This is my code:
let nums = [3, 3];
let target = 6;
var twoSum = (nums, target) => {
for (let i = 0; i < nums.length; i++) {
let result = [];
if (target === nums[i] + nums[i + 1]) {
result.push(nums.indexOf(nums[i]));
result.push(nums.indexOf(nums[i + 1]));
return result;
}
}
};
console.log(twoSum(nums, target));
Output is [0,0] here, instead of [0,1].
Where did the logic fail? I clearly pushed nums[i + 1] to the array as the second value.
Your algorithm is incorrect because the elements need not be adjacent to each other. Instead, you should use a nested loop to add each element of the array with every element after it.
var twoSum = (nums, target) => {
for (let i = 0; i < nums.length; i++) {
for(let j = i + 1; j < nums.length; j++){
if(nums[i] + nums[j] === target) return [i, j];
}
}
};
I am learning about heaps and I wanted to implement the heap sort algorithm in Javascript using MinHeap.
The issue is that I keep getting a non-sorted array.
I even tried to just translate a working algorithm from C++ to Javascript.
Orginal algorithm link: https://www.geeksforgeeks.org/heap-sort-for-decreasing-order-using-min-heap/
C++:
// To heapify a subtree rooted with node i which is
// an index in arr[]. n is size of heap
void heapify(int arr[], int n, int i)
{
int smallest = i; // Initialize smalles as root
int l = 2 * i + 1; // left = 2*i + 1
int r = 2 * i + 2; // right = 2*i + 2
// If left child is smaller than root
if (l < n && arr[l] < arr[smallest])
smallest = l;
// If right child is smaller than smallest so far
if (r < n && arr[r] < arr[smallest])
smallest = r;
// If smallest is not root
if (smallest != i) {
swap(arr[i], arr[smallest]);
// Recursively heapify the affected sub-tree
heapify(arr, n, smallest);
}
}
// main function to do heap sort
void heapSort(int arr[], int n)
{
// Build heap (rearrange array)
for (int i = n / 2 - 1; i >= 0; i--)
heapify(arr, n, i);
// One by one extract an element from heap
for (int i = n - 1; i >= 0; i--) {
// Move current root to end
swap(arr[0], arr[i]);
// call max heapify on the reduced heap
heapify(arr, i, 0);
}
}
Javascipt (translated code):
function swap(arr, i, j){
const c = arr[i];
arr[i] = arr[j];
arr[j] = c;
}
function heapify(arr, n, i)
{
let smallest = i; // Initialize smalles as root
let l = 2 * i + 1; // left = 2*i + 1
let r = 2 * i + 2; // right = 2*i + 2
// If left child is smaller than root
if (l < n && arr[l] < arr[smallest])
smallest = l;
// If right child is smaller than smallest so far
if (r < n && arr[r] < arr[smallest])
smallest = r;
// If smallest is not root
if (smallest != i) {
swap(arr[i], arr[smallest]);
// Recursively heapify the affected sub-tree
heapify(arr, n, smallest);
}
}
// main function to do heap sort
function heapSort(arr, n)
{
// Build heap (rearrange array)
for (let i = n / 2 - 1; i >= 0; i--)
heapify(arr, n, i);
// One by one extract an element from heap
for (let i = n - 1; i >= 0; i--) {
// Move current root to end
swap(arr[0], arr[i]);
// call max heapify on the reduced heap
heapify(arr, i, 0);
}
}
when I try with this array arr = [1,2,7,3,5], the heapSort algorithm returns this table [ 1, 2, 7, 3, 5 ];
Can you please help me figure out what's wrong with the JS implementation?
thank you in advance!
This code should go fine:
const heapify = (arr, length, i) => {
let largest = i
const left = i * 2 + 1
const right = left + 1
if (left < length && arr[left] > arr[largest]) {
largest = left
}
if (right < length && arr[right] > arr[largest]) {
largest = right
}
if (largest !== i) {
[arr[i], arr[largest]] = [arr[largest], arr[i]]
heapify(arr, length, largest)
}
return arr
}
const heapSort = arr => {
const length = arr.length
let i = Math.floor(length / 2 - 1)
let k = length - 1
while (i >= 0) {
heapify(arr, length, i)
i--
}
while (k >= 0) {
[arr[0], arr[k]] = [arr[k], arr[0]]
heapify(arr, k, 0)
k--
}
return arr
}
const arr = [4, 6, 3, 2, 9];
sortedArr = heapSort(arr);
console.log("Sorted array is \n", sortedArr)
I took it from here. Take a look at the post if you are more interested in how it's implemented. It's very well explained.
UPDATE
Ok so, about your code, I see exactly 2 problems:
You are incorrectly using the "swap" function. Just change swap(arr[i], arr[smallest] by swap(arr, i, smallest); and swap(arr[0], arr[i]) by swap(arr, 0, i). Also, if you want to use the latest ES6 features you can swap elements in an array without implementing that "swap" function, just like this: [arr[0], arr[2]] = [arr[2], arr[0]] (this will swap the element at position 0 with the element at position 2). This is called destructuring assignment.
In the first for loop in your "heapSort" function, initialize i variable to an integer (notice that n / 2 could be a float). You can do it like this: let i = Math.floor(n / 2 - 1).
Here I leave you the fixed code. I've executed it by myself and it works:
function swap(arr, i, j){
const c = arr[i];
arr[i] = arr[j];
arr[j] = c;
}
function heapify(arr, n, i)
{
let smallest = i; // Initialize smallest as root
let l = 2 * i + 1; // left = 2*i + 1
let r = 2 * i + 2; // right = 2*i + 2
// If left child is smaller than root
if (l < n && arr[l] < arr[smallest])
smallest = l;
// If right child is smaller than smallest so far
if (r < n && arr[r] < arr[smallest])
smallest = r;
// If smallest is not root
if (smallest != i) {
swap(arr, i, smallest);
// Recursively heapify the affected sub-tree
heapify(arr, n, smallest);
}
}
// main function to do heap sort
function heapSort(arr, n)
{
// Build heap (rearrange array)
for (let i = Math.floor(n / 2 - 1); i >= 0; i--)
heapify(arr, n, i);
// One by one extract an element from heap
for (let i = n - 1; i >= 0; i--) {
// Move current root to end
swap(arr, 0, i);
// call max heapify on the reduced heap
heapify(arr, i, 0);
}
}
const arr = [4, 6, 3, 2, 9];
heapSort(arr, arr.length);
console.log("Sorted array is \n", arr)
Here is my version of heapsort.
This is non-recursive solution and modifies the original array.
function swap(arr, i, j) {
const tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
function heapify(arr, start = 0) {
for(let i = start;i < arr.length; i++) {
let j = i;
let root = start + Math.floor((j-start)/2);
while(( root >= start ) && (arr[root] < arr[j])) {
swap(arr, root, j);
j = root;
root = start + Math.floor((j-start)/2);
}
}
}
function heapSort(arr) {
for(let i = 0; i < arr.length;i++)
heapify(arr, i);
}
const arr = [1,2,8000,3,4,5,-1,200000,8000,-1,20000];
heapSort(arr);
console.log(arr);
const HeapSort = (arg) => {
const Income_arr = [...arg];
const Output_arr = [];
const InnerSort = () => {
const length = Income_arr.length;
for (let i = 0; i < Income_arr.length - 1; i++) {
let max = i;
const left = i + 1;
const right = i + 2;
// will change '>' or '<' depends on which order we want, like either descending or ascending order
if (i <= length && Income_arr[i] > Income_arr[left]) {
// swapping the array
[Income_arr[i], Income_arr[left]] = [Income_arr[left], Income_arr[i]];
}
if (i <= length && Income_arr[i] > Income_arr[right]) {
// swapping the array
[Income_arr[i], Income_arr[right]] = [Income_arr[right], Income_arr[i]];
}
}
Output_arr.push(Income_arr.shift()); // Add the largest Number in output_arr & remove the largest Number
return Income_arr;
};
for (let i = arg.length - 1; i >= 0; i--) {
// Run untill array length ends
InnerSort(); // To Find the largest number
}
console.log(Output_arr)
return Output_arr;
};
HeapSort([16, 20, 99, 34, 17, 15]);
HeapSort([16, 20, -99, 34, 17, 15]);
HeapSort([4, 20, 9, 34, 2, 15]);
const HeapSort = (arg) => {
const Income_arr = [...arg];
const Output_arr = [];
const InnerSort = () => {
const length = Income_arr.length;
for (let i = 0; i < Income_arr.length - 1; i++) {
let max = i;
const left = i + 1;
const right = i + 2;
// will change '>' or '<' depends on which order we want, like either descending or ascending order
if (i <= length && Income_arr[i] > Income_arr[left]) {
// swapping the array
[Income_arr[i], Income_arr[left]] = [Income_arr[left], Income_arr[i]];
}
if (i <= length && Income_arr[i] > Income_arr[right]) {
// swapping the array
[Income_arr[i], Income_arr[right]] = [Income_arr[right], Income_arr[i]];
}
}
Output_arr.push(Income_arr[length - 1]); // Add the largest Number in output_arr
Income_arr.pop(); // Remove the largest Number
return Income_arr;
};
for (let i = arg.length - 1; i >= 0; i--) {
// Run untill array length ends
InnerSort(); // To Find the largest number
}
console.log(Output_arr)
return Output_arr;
};
HeapSort([16, 20, 99, 34, 17, 15]);
HeapSort([16, 20, -99, 34, 17, 15]);
HeapSort([4, 20, 9, 34, 2, 15]);
I want to find all possible arrays -of non-negative numbers- that sum up to -at most- N in JavaScript:
function findArrays(maxSize, maxSum){}
Example input: findArrays(3, 10)
Some acceptable outputs: (not writing all as it would be too long)
[[0], [0,0,0], [10,0,0], [1,9], [1,2,3] /*, ... */]
What I tried so far:
I know it looks like homework but it's not :) I can think of a solution that simply generates all (size*maxSum) possible arrays of acceptable sizes and then iterate through them to check if sum is greater than maxSum. However, I think this solution is very bad in terms of performance as maxSum gets bigger. I'm looking for a more efficient implementation but I just don't know where to start.
My "bad" solution
function getNextArray(r,maxVal){
for(var i=r.length-1;i>=0;i--){
if(r[i]<maxVal){
r[i]++;
if(i<r.length-1){
r[i+1]=0;
}
break;
}
}
return r;
}
function getAllArraysOfSize(size, maxVal){
var arrays=[],r=[],i;
for(i=0;i<size;i++){
r[i]=0;
}
while(r.reduce((a, b) => a + b, 0) < (maxVal*size)){
r = getNextArray(r.slice(),maxVal);
arrays.push(r);
}
return arrays;
};
function findArrays(maxSize, maxSum){
var allArrays=[],arraysOfFixedSize=[],acceptableArrays=[],i,j;
for(i=1; i<=maxSize; i++){
arraysOfFixedSize=getAllArraysOfSize(i,maxSum);
for(j=0; j<arraysOfFixedSize.length; j++){
allArrays.push(arraysOfFixedSize[j]);
}
}
for(i=0; i<allArrays.length; i++){
if(allArrays[i].reduce((a, b) => a + b, 0) <= maxSum){
acceptableArrays.push(allArrays[i]);
}
}
return acceptableArrays;
};
You can use recursion and a generator. The number of outputs grows quickly for higher valued arguments, so I keep them low here:
function * findArrays(maxSize, maxSum) {
let arr = [];
function * recur(maxSum) {
let k = arr.length;
yield [...arr]; // or: if (k) yield [...arr]
if (k === maxSize) return;
for (let i = 0; i <= maxSum; i++) {
arr[k] = i;
yield * recur(maxSum - i);
}
arr.length = k;
}
yield * recur(maxSum);
}
// demo
for (let arr of findArrays(2, 4))
console.log(JSON.stringify(arr));
NB: this also produces the empty array, which makes sense. If you want to avoid this, then just check that you don't yield an empty array.
If you prefer working with plain functions instead of generators, then translate the innermost yield expression to a push unto a result array, as follows:
function findArrays(maxSize, maxSum) {
let arr = [];
let result = []; // <--- will collect all the subarrays
function recur(maxSum) {
let k = arr.length;
result.push([...arr]);
if (k === maxSize) return;
for (let i = 0; i <= maxSum; i++) {
arr[k] = i;
recur(maxSum - i);
}
arr.length = k;
}
recur(maxSum);
return result;
}
// demo
for (let arr of findArrays(2, 4))
console.log(JSON.stringify(arr));
i hope this is helpful
const data = [[0],[0,0,0],[10,0,0],[1,9],[1,2,3]];
function findArrays(maxSize, maxSum){
return data.reduce(
(acc, value) => {
if (value.length <= maxSize) {
const tempValue = value;
const sum = tempValue.reduce((acc, val) => val >= 0 ? acc + val : 0, 0);
if (sum <= maxSum && sum > 0) acc.push(value);
}
return acc
}, []
)
}
console.log(findArrays(3, 10));
Please help me to solve this leetcode problem using javascript as I am a beginner and dont know why this code is not working
Ques: Given an array of integers where 1 ≤ a[i] ≤ n (n = size of array), some elements appear twice and others appear once.
Find all the elements of [1, n] inclusive that do not appear in this array.
var findDisappearedNumbers = function (nums) {
var numLength = nums.length;
nums.sort(function (a, b) { return a - b });
for (var i = 0; i < nums.length - 1; i++) {
if (nums[i + 1] === nums[i]) {
nums.splice(i, 1);
}
}
for (var k = 0; k < nums.length; k++) {
for (var j = 1; j <= numLength; j++) {
if (nums[k] !== j) {
return j;
}
}
}
};
if there is any error in my code please let me know;
i have done the following thing
first i have sorted the array in ascending order
then i have cut all the duplicate elements
then i have created loop that will check if nums[k] !== j ;
and it will return j which is the missing number;
for example this is the testcase [4,3,2,7,8,2,3,1]
first my code will sort this in ascending order [1,2,2,3,3,4,7,8]
then it will remove all duplicate elements and it will return [1,2,3,4,,7,8]
and then it will check nums[k] is not equal to j and it will print j
I think it'd be easier to create a Set of numbers from 1 to n, then just iterate through the array and delete every found item from the set:
var findDisappearedNumbers = function(nums) {
const set = new Set();
for (let i = 0; i < nums.length; i++) {
set.add(i + 1);
}
for (const num of nums) {
set.delete(num);
}
return [...set];
};
console.log(findDisappearedNumbers([4,3,2,7,8,2,3,1]));
To fix your existing code, I'm not sure what the logic you're trying to implement in the lower section, but you can iterate from 1 to numLength (in the outer loop, not the inner loop) and check to see if the given number is anywhere in the array. Also, since you're mutating the array with splice while iterating over it in the upper loop, make sure to subtract one from i at the same time so you don't skip an element.
var findDisappearedNumbers = function(nums) {
var numLength = nums.length;
nums.sort(function(a, b) {
return a - b
});
for (var i = 0; i < nums.length - 1; i++) {
if (nums[i + 1] === nums[i]) {
nums.splice(i, 1);
i--;
}
}
const notFound = [];
outer:
for (var j = 1; j < numLength; j++) {
for (var k = 0; k < nums.length; k++) {
if (nums[k] === j) {
continue outer;
}
}
notFound.push(j);
}
return notFound;
};
console.log(findDisappearedNumbers([4, 3, 2, 7, 8, 2, 3, 1]));
#CertainPerformance certainly cracked it again using the modern Set class. Here is a slighly more conservative approach using an old fashioned object:
console.log(missingIn([4,3,2,7,8,2,3,1]));
function missingIn(arr){
const o={};
arr.forEach((n,i)=>o[i+1]=1 );
arr.forEach((n) =>delete o[n] );
return Object.keys(o).map(v=>+v);
}
My solution for the problem to find the missing element
var findDisappearedNumbers = function(nums) {
const map={};
const result=[];
for(let a=0;a<nums.length;a++){
map[nums[a]]=a;
}
for(let b=0;b<nums.length;b++){
if(map[b+1]===undefined){
result.push(b+1)
}
}
return result;
};
Example 1:
Input: nums = [4,3,2,7,8,2,3,1]
Output: [5,6]
Example 2:
Input: nums = [1,1]
Output: [2]
I'm trying to solve a basic problem in Hackerearth Given an array A of size N, you need to find the number of ordered pairs (i,j) such that i < j and A[i] > A[j].
I was able to find out an idea actually implemented it by having a global variable. But having a global value is not a good practice, hence I tried to pass it as parameter and I couldn't able to solve it. Am stuck with keeping an already returned value and adding an updated value to it.
// let ans = 0;
let mergeSort = (left, right, arr, ans) => {
let i = 0,
j = 0,
k = 0;
let leftLen = left.length,
rightLen = right.length;
while (i < leftLen && j < rightLen) {
if (left[i] <= right[j]) {
arr[k] = left[i];
i++;
} else {
arr[k] = right[j];
ans += leftLen - i;
j++;
}
k++;
}
while (i < leftLen) {
arr[k] = left[i];
i++;
k++;
}
while (j < rightLen) {
arr[k] = right[j];
j++;
k++;
}
return { arr, ans };
};
let divideArray = (arr, ans) => {
if (arr.length < 2) return { arr, ans };
let N = arr.length;
let mid = Math.round(N / 2);
let left = arr.slice(0, mid);
let right = arr.slice(mid, N);
ans = ans;
divideArray(left, ans);
divideArray(right, ans);
let blabla = mergeSort(left, right, arr, ans);
return blabla;
};
let merge = (arr, ans) => {
let res = divideArray(arr, ans);
return res;
};
function main(input) {
let arr = [1, 4, 3, 2, 5];
let ans = 0;
let output = merge(arr, ans);
console.log('Final', output);
}
main();
In mergeSort function When the input of the left array is [1,4] and the right array is [3] the ans will be updated as 1, also when the left array is [1,3,4] and right is [2,5] the ans will be updated as 2. I would like to add both this ans values and return it as 3. But somewhere am making a mistake while returning. Any help will be appreciated.
JsFiddle
EDIT:
Please note that am trying to achieve it via MergeSort and recursion i know in lot of other ways i can solve this problem for instance i have clearly mentioned i had solved it by having a global variable,which is not a good practice so please provide me a solution only via recursion and merge sort
There is no need to pass an inversion count to divideArray(), it only needs to return a sub_count = left_count + right_count + merged_count. The sub_counts originate from each instance of merging and will be accumulated as recursion returns sub-counts back up the call chain to produce a total_count.
Example of an optimized top down merge sort updated to return an inversion count. A helper function (mergesort()) does a one time allocation of an auxiliary array (aux). To avoid unneeded copying of data, two mutually recursive functions are used, sortarrtoarr() sorts data from arr[] back to arr[], while sortarrtoaux() sorts data from arr[] to aux[]. Each of the mutually recursive functions calls the other in order to change the direction of merge based on the level of recursion.
function merge(arr, aux, bgn, mid, end) {
var i = bgn;
var j = mid;
var k = bgn;
var cnt = 0; // count of inversions
while(true){
if(arr[i] <= arr[j]){ // if left element <= right element
aux[k++] = arr[i++]; // copy left element
if(i < mid) // if not end of left run
continue; // continue back to while
do // else copy rest of right run
aux[k++] = arr[j++]; // and break
while(j < end);
break;
} else { // else left element > right element
cnt += mid - i; // arr.slice(i,mid) is > arr[j]
aux[k++] = arr[j++]; // copy right element
if(j < end) // if not end of right run
continue; // continue back to while
do // else copy rest of left run
aux[k++] = arr[i++]; // and break
while(i < mid);
break;
}
}
return cnt; // return inversion count
}
// sort from arr[] to aux[]
function sortarrtoaux(arr, aux, bgn, end) {
if ((end-bgn) < 2){ // if only 1 element
aux[bgn] = arr[bgn]; // copy it to aux
return 0; // return inversion count == 0
}
var cnt = 0; // inversion count = 0
var mid = Math.floor(bgn + (end - bgn) / 2);
cnt += sortarrtoarr(arr, aux, bgn, mid); // sort left arr back to arr
cnt += sortarrtoarr(arr, aux, mid, end); // sort right arr back to arr
cnt += merge(arr, aux, bgn, mid, end); // merge arr to aux
return cnt; // return inversion count
}
// sort from arr[] back to arr[]
function sortarrtoarr(arr, aux, bgn, end) {
if ((end-bgn) < 2) // if only 1 element
return 0; // return inversion count == 0
var cnt = 0; // inversion count = 0
var mid = Math.floor(bgn + (end - bgn) / 2);
cnt += sortarrtoaux(arr, aux, bgn, mid); // sort left arr to aux
cnt += sortarrtoaux(arr, aux, mid, end); // sort right arr to aux
cnt += merge(aux, arr, bgn, mid, end); // merge aux to arr
return cnt; // return inversion count
}
// entry function for mergesort
function mergesort(arr) {
if(arr.length < 2) // if less than 2 elements
return 0; // return inversion count == 0
var aux = new Array(arr.length); // allocate aux[] and start merge sort
return sortarrtoarr(arr, aux, 0, arr.length);
}
var arr = [8, 6, 7, 5, 3, 0, 9];
var cnt = mergesort(arr);
console.log(cnt);
for (i = 1; i < arr.length; i++) {
if(arr[i-1] > arr[i]){
console.log('error');
break;
}
}
Scott's answer offers a functional approach. Generators, function* below, offer another capable and flexible way of encoding this kind of problem -
const descendingPairs = function* (a = [])
{ for (const i of range(0, a.length)) // for all (0 <= i < a.length)
for (const j of range(0, a.length)) // for all (0 <= i < a.length)
if (i < j) // such that i < j
if (a[i] > a[j]) // and a[i] > a[j]
yield [ a[i], a[j] ] // output descending pair
}
We can optimise this by using i as the input for the j range's start -
const descendingPairs = function* (a = [])
{ for (const i of range(0, a.length)) // for all (0 < i < a.length)
for (const j of range(i + 1, a.length)) // for all (i < j < a.length)
if (a[i] > a[j]) // such that a[i] > a[j]
yield [ a[i], a[j] ] // output descending pair
}
range is nicely-encoded using a generator as well -
const range = function* (start = 0, stop = 0)
{ for (let x = start; x < stop; x++)
yield x
}
We can output the results of each descending pair -
const input =
[ 1, 4, 3, 2, 5 ]
for (const pair of descendingPairs(input))
console.log(pair)
// [ 4, 3 ]
// [ 4, 2 ]
// [ 3, 2 ]
Or we can collect all pairs into an array -
Array.from(descendingPairs(input))
// => [ [ 4, 3 ], [ 4, 2 ], [ 3, 2 ] ]
Or we can simply count them -
Array.from(descendingPairs(input)).length
// => 3
Expand the snippet below to verify the results in your own browser -
const range = function* (start = 0, stop = 0)
{ for (let x = start; x < stop; x++)
yield x
}
const descendingPairs = function* (a = [])
{ for (const i of range(0, a.length))
for (const j of range(i, a.length))
if (a[i] > a[j])
yield [ a[i], a[j] ]
}
const input =
[ 1, 4, 3, 2, 5 ]
console.log(Array.from(descendingPairs(input)))
// [ [ 4, 3 ], [ 4, 2 ], [ 3, 2 ] ]
console.log(Array.from(descendingPairs(input)).length)
// 3
I'm having a hard time figuring why you are writing this with code that's all about a mergesort. It seems to me that all you need to do is to generate the index pairs where j > i (a fairly easy task) and then count those for which A[i] > A[j]. Recursion is a fine way (though by no means the only easy way) to create those index pairs. The rest is a filter/length combination or a reduce.
Here's one variation:
const countDescendingPairs = (xs) =>
xs .map ((x, i) => xs .slice (i + 1) .filter(y => x > y) .length)
.reduce ((a, b) => a + b, 0)
console .log (
countDescendingPairs ([8, 6, 7, 5, 3, 0, 9])
)
But there are many simple alternatives.
And if you wanted to retrieve those pairs, it's a straightforward modification:
const descendingPairs = (xs) =>
xs .flatMap (
(x, i) =>
xs .slice (i + 1)
.filter (y => x > y)
.map ((y) => ({x, y}))
)
console .log (
descendingPairs ([8, 6, 7, 5, 3, 0, 9])
)
Updated to add flatMap and to remove the incorrect indices from the second version. (You can't filter, then expect the old index to still work!)
https://codepen.io/Santhoshsanz/pen/dybedgm?editors=1112
// let ans = 0;
let mergeSort = (left, right, arr, ans) => {
// console.log(left)
// console.log("*****")
// console.log(right)
// console.log("*****£££")
let i = 0,
j = 0,
k = 0;
let leftLen = left.length,
rightLen = right.length;
while (i < leftLen && j < rightLen) {
if (left[i] <= right[j]) {
arr[k] = left[i];
i++;
} else {
arr[k] = right[j];
ans += leftLen - i;
j++;
}
k++;
}
while (i < leftLen) {
arr[k] = left[i];
i++;
k++;
}
while (j < rightLen) {
arr[k] = right[j];
j++;
k++;
}
return { arr, ans };
};
let divideArray = (arr, ans) => {
if (arr.length < 2) return { arr, ans };
let N = arr.length;
let mid = Math.round(N / 2);
let left = arr.slice(0, mid);
let right = arr.slice(mid, N);
ans = ans;
let lans=divideArray(left, ans).ans;
let bAns=divideArray(right, ans).ans;
// console.log(bAns)
let blabla= mergeSort(left, right, arr, lans+bAns);
return blabla
};
let merge = (arr, ans) => {
let res=0+ divideArray(arr, ans).ans;
// console.log("asdad")
// console.log(res)
return res;
};
function main(input) {
let arr = [1,4,3,2,5];
let ans = 0;
let output = merge(arr, ans);
console.log('Final', output);
}
main();
I have persisted the count val inside your divide array and used it to merge the 2 counts from the split array i.e left and right direction split