Home > Back-end >  how can you improve this algorithm for finding the smallest positive integer not in an array
how can you improve this algorithm for finding the smallest positive integer not in an array

Time:06-15


function findSmallestPositiveInteger(A) {


    // sort array from smallest to largest number
     A.sort((a, b) => a - b)
     
    // remove duplicates because they just would take memory
    const noDups =Array.from(new Set(A).keys())
    
    let smallestPositiveInteger=1
    
    let previous = noDups[0]
    if(previous <= smallestPositiveInteger && previous>0)
            smallestPositiveInteger  
    
    for(let i =1;i<noDups.length;i  ){
      const v = noDups[i]
      if(previous > 0){
         const diffWithPrevious = v - previous
         // the logic for this early return is that we might not need to traverse
         // the whole array for example imagine this example 
         // [1,2,5,6,8,...n]
         // its clear that there is a gap between 5 and 2 so we can just 
         // conclude that 2 1 is the smallest postive integer not in our array 
         if(diffWithPrevious > 1) return previous  1;      
      }
      
      // check if smallest positive integer in array is not 1
      // if so return 1 
      if(previous == 0 && v > 1 ) return 1
      
      if(v <= smallestPositiveInteger && v>0)
         smallestPositiveInteger  
       previous = v
    }

    return smallestPositiveInteger
}


const arr =[-1,-2,1,3,10,9,3,2,3,3,10,2,7,99,100,10000,500,50,60,70,33]

console.log(findSmallestPositiveInteger(arr))

CodePudding user response:

Put the integers from the array into a lookup structure, and then just try natural numbers starting from 1 until you find one that was not in the array:

function findSmallestPositiveInteger(arr) {
    const lookup = new Set(arr);
    let i = 1;
    while (lookup.has(i)) {
        i  ;
    }
    return i;
}

CodePudding user response:

I guess I'd go about it in the following manner.

  1. sort the array, treating each element as a number (arr.sort treats as a strnig without a compare function)
  2. set a target variable to 1
  3. stepping through the elements, if my target is found, increment the number we now look for
  4. when finished looping, return the value that we were last searching for

A little something like this I supppose.

"use strict";
window.addEventListener('load', onl oad, false);
function onl oad(evt)
{
    let arr =[-1,-2,1,3,10,9,3,2,3,3,10,2,7,99,100,10000,500,50,60,70,33];
    test(arr);
}

function test(arr)
{
    arr = arr.sort(function(a, b){return a-b});
    let nextTgt = 1;
    arr.forEach( el => { if (el==nextTgt) nextTgt   ;} );
    console.log(arr);
    console.log(nextTgt);
}

EDIT: A vast improvement would break from the loop if the current array element being examined is larger than the current search target.

function test2(arr)
{
    arr = arr.sort(function(a, b){return a-b});
    let nextTgt = 1;
    for (var i=0,n=arr.length; i<n; i  )
    {
        if (arr[i] == nextTgt)
            nextTgt  ;
        else if (arr[i]>nextTgt)
            break;
    }
    console.log(arr);
    console.log(nextTgt);
}

CodePudding user response:

const smallestPositiveInt = (arr) => {
   // sort and remove negatives, duplicates, float
   const sort = Array.from(new Set(arr))
     .filter(n => n >= 1 && Number.isInteger(n))
     .sort((a,b) => a - b);
  
   let smallestInt = 1;
   // if the smallest num isn't 1 we return 1 before any other check
   if(sort[0] !== 1) {
     return smallestInt
   } else {
       // add until you get to a positive int that doesn't exist in the arr, then return it
       while(sort.includes(smallestInt)) {
         smallestInt  ;
       }
       return smallestInt
   }
}

CodePudding user response:

You could remove the sorting and build out an object where the keys are the values in the array. Then just iterate over the arrays length and break at the first missing number.

function findSmallestPositiveInteger(A) {
    const noDups = Object.assign({} , ...A.map((x) => ({[x]: true})));
    let smallestPositiveInteger = 1
    for (let i = 1; i < A.length; i  ) {
        if (typeof noDups[i] == 'undefined') {
            smallestPositiveInteger = i;
            break;
        }
    }

    return smallestPositiveInteger;
}


const arr = [-1, -2, 1, 3, 10, 9, 3, 2, 3, 3, 10, 2, 7, 99, 100, 10000, 500, 50, 60, 70, 33]

console.log(findSmallestPositiveInteger(arr))

CodePudding user response:

Experimental Approach

This method trades space complexity for time, but should be extremely fast. The reason we can do this in Javascript is because arrays can be abused like hashes

const myArray = [-1,-2,1,3,10,9,3,2,3,3,10,2,7,99,100,10000,500,50,60,70,33, Infinity]

function findSmallestPositive(arr) {
  // create a new array which uses the index
  const indexed = Array(arr.length)
  // add elements as the index of the new array
  for (let i=0; i<arr.length; i  ) {
    if (arr[i] > 0) indexed[arr[i]] = 1
  }
  // return the first element above 0 that is missing
  for (let i=1; i<indexed.length; i  ) {
    if (!indexed[i]) return i
  }
  // return undefined if nothing found
  return undefined
}

console.time()
console.log(findSmallestPositive(myArray))
console.timeEnd()

The idea here being we can treat arrays as sorted hash maps, then we just need to find the first index which is undefined.

NOTE: arrays can be implemented under the hood in a variety of ways depending on the contents, platform and engine. Here is a decent deep dive into how the V8 engine handles arrays internally:

https://ryanpeden.com/how-do-javascript-arrays-work-under-the-hood/#:~:text=In V8, if your array,by an array of doubles.

you now have a sparse array. If is not too spare, it’ll still be backed by an array, with empty array indices replaced with a ‘hole’ value. If you look at V8’s C array source (linked below), you’ll see calls to element->is_the_hole(i). If an array is very sparse, it’ll no longer be backed by an array in memory. Instead, it will be backed by a dictionary/hashtable, and it’ll take longer to both access elements and iterate through the array.

CodePudding user response:

First of all, filtering is O(n), therefore anything like negative numbers or non-integers are irrelevant. After that, the highest number it can be is at most the array's length plus one (and you are guaranteed to find one from one to that number).

You can track every one of these in O(n):

const helper = arr => {
  const marks = Array(arr.length   1).fill(false);
  for (const n of arr) {
    if (n < marks.length) marks[n - 1] = true;
  }
  return marks.findIndex(e => !e)   1;
};

const findSmallestPositiveInteger = arr => helper(arr.filter(e => Number.isInteger(e) && e > 0));

const arr = [-1, -2, 1, 3, 10, 9, 3, 2, 3, 3, 10, 2, 7, 99, 100, 10000, 500, 50, 60, 70, 33]

console.log(findSmallestPositiveInteger(arr))

CodePudding user response:

Here's a solution which does not sort, runs in O(n) (it scans the entire array exactly twice) and doesn't require any additional temporary array. But it does rearrange the original array.

The isInteger eliminates the possibility of treating a string as an integer, as JavaScript likes to do, which is why the sample returns 5; the function doesn't consider the string '5' to be an integer. I think that's correct, but YMMV.

const myArray = [-1,-2,1,3,"5",10,9,3,2,3,4,10,2,7,99,100,10000,500,50,60,70,33, Infinity]

function findSmallestPositive(arr) {
  for (let idx=0; idx<arr.length; idx  ) {
    const val = arr[idx];
    if (Number.isInteger(val) && 0 <= val && val < arr.length) {
      arr[idx] = arr[val-1];
      arr[val-1] = val;
    }
  }
  // At this point, the values are rearranged so that arr[i-1] is i
  // if and only if i is in the array. Now scan again to find the
  // smallest missing value.
  for (let idx=1; idx<=arr.length; idx  ) {
    if (!(Number.isInteger(arr[idx-1]) && arr[idx-1] == idx)) {
      return idx;
    }
  }
  return arr.length   1;
}

console.time()
console.log(findSmallestPositive(myArray))
console.timeEnd()

  • Related