Given an array of integers, find the first missing positive integer in linear time and constant space

百般思念 提交于 2020-07-17 05:41:10

问题


In other words, find the lowest positive integer that does not exist in the array. The array can contain duplicates and negative numbers as well. This question was asked by Stripe in it's programming interview. I have devised a solution for the same as below:

#include<bits/stdc++.h>
using namespace std;

int main(){
    int arr[]={1,-1,-5,-3,3,4,2,8};
    int size= sizeof(arr)/sizeof(arr[0]);
    sort(arr, arr+size);
    int min=1;

    for(int i=0; i<size; i++){
        if(arr[i]>min) break;
        if(arr[i]==min) min=min+1;
    }
    cout<<min;
    return 0;
}

Here, I am first sorting the array, and then traversing the array once. Before traversing the array, I have initialized a variable named "min" as 1. Now, while traversing the array, when we get an integer that is equal to min, we simply increment the value of min. This ensures that the min variable hold the latest least positive integer that has not occurred yet. Can you think of any better approach? Thanks in advance.


回答1:


Assuming the array can be modified,

  1. We divide the array into 2 parts such that the first part consists of only positive numbers. Say we have the starting index as 0 and the ending index as end(exclusive).

  2. We traverse the array from index 0 to end. We take the absolute value of the element at that index - say the value is x.

    1. If x > end we do nothing.
    2. If not, we make the sign of the element at index x-1 negative.
  3. Finally, we traverse the array once more from index 0 to end. In case we encounter a positive element at some index, we output index + 1. This is the answer. However, if we do not encounter any positive element, it means that integers 1 to end occur in the array. We output end + 1.

It can also be the case that all the numbers are non-positive making end = 0. The output end + 1 = 1 remains correct.

All the steps can be done in O(n) time and using O(1) space.

Example:

Initial Array:            1 -1 -5 -3 3 4 2 8
Step 1 partition:         1 8 2 4 3 | -3 -5 -1, end = 5

In step 2 we change the signs of the positive numbers to keep track of which integers have already occurred. For example, here array[2] = -2 < 0, it suggests that 2 + 1 = 3 has already occurred in the array. Basically, we change the value of the element having index i to negative if i+1 is in the array.

Step 2 Array changes to: -1 -8 -2 -4 3 | -3 -5 -1

In step 3, if some value array[index] is positive, it means that we did not find any integer of value index + 1 in step 2.

Step 3: Traversing from index 0 to end, we find array[4] = 3 > 0
        The answer is 4 + 1 = 5



回答2:


PMCarpan's algorithm works.

I think your approach works, but you should specify the type of sort you're doing so that it is clear it is a linear sort and not necessarily a full sort of the entire array. This results in O(N) time without using any space.

Scan the array, as you're scanning if the value in your current index is less than the length of the array then swap it with the value currently in that index. You must continue swapping until it no longer makes sense to swap at each index. Then at the end do one more scan until you find an index that isn't correct.

Here's some working python code, although python is not the place to do this sort of thing, lol.

def sortOfSort(arr) :
    for index in range(len(arr)) :
        checkValue = arr[index]

        while(checkValue > 0 and checkValue != index and checkValue < len(arr) and arr[checkValue] != checkValue) :
            arr[index] = arr[checkValue]
            arr[checkValue] = checkValue
            checkValue = arr[index]

    return arr[1:] + [arr[0]]

def findFirstMissingNumber(arr) :
    for x in range(len(arr)) :
        if (x+1 != arr[x]) :
            return x+1
    return len(arr) + 1

the return arr[1:] part is because based on your description we aren't including zero as a starting point.




回答3:


I solved the problem using set in python3. It is very simple 6LOC. time complexity: O(n).

Remember: Membership check in set is O(1)

def first_missing_positive_integer(arr):
    arr = set(arr)
    for i in range(1, len(arr)+2):
        if i not in arr:
            return i



回答4:


Here is a C implementation
INPUT

#include <stdio.h>
#include <stdlib.h>
//Here we separate the positive and negative number
int separate (int arr[], int size)
{
    int j = 0, i , temp;
    for(i = 0; i < size; i++)
    {
    if (arr[i] <= 0)
    {
        /*Here we using bitwise operator to swap the
        numbers instead of using the temp variable*/
         arr[j] = arr[j]^arr[i];
         arr[i] = arr[j]^arr[i];
         arr[j] = arr[j]^arr[i];
         j++;
    }
    }
    printf("First We Separate the negetive and positive number \n");
    for( i = 0 ; i <size ; i++)
    {
        printf("Array[%d] = %d\n",i,arr[i]);
    }
    return j;
}
int findMissingPositive(int arr[], int size)
{
printf("Remove the negative numbers from array\n");
int i;
for( i = 0 ; i <size ; i++)
{
        printf("Array[%d] = %d\n",i,arr[i]);
}
for(i = 0; i < size; i++)
{
    if(abs(arr[i]) - 1 < size && arr[ abs(arr[i]) - 1] > 0)
    arr[ abs(arr[i]) - 1] = -arr[ abs(arr[i]) - 1];
}
for(i = 0; i < size; i++)
    if (arr[i] > 0)
    {
    return i+1;
    }
return size+1;
}
int findMissing(int arr[], int size)
{
int j = separate (arr, size);
return findMissingPositive(arr+j, size-j);
}
int main()
{
int size ;
printf("Enter the Value of Size of Array : ");
scanf("%d",&size);
int arr[size];
printf("Enter the values :\n");
for( int i = 0 ; i < size ; i++)
{
    printf("Array[%d] = ",i);
    scanf("%d",&arr[i]);
}
int missing = findMissing(arr,size);
printf("The smallest positive missing number is %d ", missing);
return 0;
}


OUTPUT
Enter the Value of Size of Array : 8
Enter the values :
Array[0] = 1
Array[1] = -1
Array[2] = -5
Array[3] = -3
Array[4] = 3
Array[5] = 4
Array[6] = 2
Array[7] = 8
First We Separate the negetive and positive number
Array[0] = -1
Array[1] = -5
Array[2] = -3
Array[3] = 1
Array[4] = 3
Array[5] = 4
Array[6] = 2
Array[7] = 8
Remove the negative numbers from array
Array[0] = 1
Array[1] = 3
Array[2] = 4
Array[3] = 2
Array[4] = 8
The smallest positive missing number is 5
Process returned 0 (0x0)   execution time : 27.914 s
Press any key to continue.

 /*
        How work :
        [if(abs(arr[i]) - 1 < size && arr[ abs(arr[i]) - 1] > 0)
        arr[ abs(arr[i]) - 1] = -arr[ abs(arr[i]) - 1];]
        before: arr = { 7, 3, 4, 5, 5, 3, 2}
    i == 0: arr[0] = 7
            arr[7-1] is 2 > 0 ~> negate
            arr = { 7, 3, 4, 5, 5, 3, -2}
    i == 1: arr[1] = 3
            arr[3-1] is 4 > 0 ~> negate
            arr = { 7, 3, -4, 5, 5, 3, -2}
    i == 2: arr[2] is -4 ~> abs for indexing
            arr[4-1] is 5 > 0 ~> negate
            arr = { 7, 3, -4,-5, 5, 3, -2}
    i == 3: arr[3] is -5 ~> abs for indexing
            arr[5-1] is 5 > 0 ~> negate
            arr = { 7, 3, -4, -5, -5, 3, -2}
    i == 4: arr[4] is -5 ~> abs for indexing
            arr[5-1] is -5 < 0 ~> print abs(-5) as duplicate
    i == 5: arr[5] is 3
            arr[3-1] is -4 < 0 ~> print abs(3) as duplicate
    i == 6: arr[6] is -2 ~> abs for indexing
            arr[2-1] is 3 > 0 ~> negate
            arr = { 7, -3, -4, -5, -5, 3, -2}

            indices of positive entries: 0, 5 ~> 1 and 6 not in original array
            indices of negative entries: 1, 2, 3, 4, 6 ~> 2, 3, 4, 5, 7 in original array
*/



回答5:


#Returns a slice containing positive numbers
def findPositiveSubArr(arr):
    negativeIndex = 0

    if i in range(len(arr)):
        if arr[i] <=0:
            arr.insert(negativeIndex, arr.pop(i))
            negativeIndex += 1
    return arr[negativeIndex:]

#Returns the first missing positive number
def findMissingPositive(positiveArr):
    l = len(positiveArr)
    for num in positiveArr:
        index = abs(num) - 1
        if index < 1 and positiveArr[index] > 0:
            positiveArr[index] *= -1

    for i in range(l):
        if positiveArr[i] > 0:
            return i+1

    return l+1

if __name__ == "__main__":
    arr = [int(x) for x in input().strip().split()]
    positiveSubArr = findPositveSubArr(arr)
    print(findMissingPositive(positiveSubArr))



回答6:


It is much simpler. (Solution is not mine)

public static int Missing(int[] a)
{
    // the idea is to put all values in array on their ordered place if possible
    for (int i = 0; i < a.Length; i++)
    {
        CheckArrayAtPosition(a, i);
    }

    for (int i = 0; i < a.Length; i++)
        if (a[i] != i + 1)
            return i + 1;
    return a.Length + 1;
}

private static void CheckArrayAtPosition(int[] a, int i)
{
    var currentValue = a[i];
    if (currentValue < 1) return; // do not touch negative values because array indexes are non-negative
    if (currentValue > a.Length) return; // do not touch values that are bigger than array length because we will not locate them anyway
    if (a[currentValue - 1] == currentValue) return; // do not need to change anything because index contain correct value already
    Swap(a, i, currentValue - 1);
    CheckArrayAtPosition(a, i); // now current position value is updated so we need to check current position again
}

private static void Swap(int[] a, int i, int j)
{
    int temp = a[i];
    a[i] = a[j];
    a[j] = temp;
}

There is a recursion but since each Swap puts 1 value to the correct place there will be <= n swaps. Linear time




回答7:


Here's a Python 3 implementation of pmcarpan's answer.

def missing_int(nums: MutableSequence[int]) -> int:
    # If empty array or doesn't have 1, return 1
    if not next((x for x in nums if x == 1), 0):
        return 1

    lo: int = 0
    hi: int = len(nums) - 1
    i: int = 0
    pivot: int = 1

    while i <= hi:
        if nums[i] < pivot:
            swap(nums, i, hi)
            hi -= 1
        elif nums[i] > pivot:
            swap(nums, i, lo)
            i += 1
            lo += 1
        else:
            i += 1

    x = 0
    while x <= hi:  # hi is the index of the last positive number
        y: int = abs(nums[x])
        if 0 < y <= hi + 1 and nums[y - 1] > 0:  # Don't flip sign if already negative
            nums[y - 1] *= -1
        x += 1

    return next((i for i, v in enumerate(nums[:hi + 1]) if v >= 0), x) + 1

Tests:

def test_missing_int(self):
    assert func.missing_int([1, 2, 1, 0]) == 3
    assert func.missing_int([3, 4, -1, 1]) == 2
    assert func.missing_int([7, 8, 9, 11, 12]) == 1
    assert func.missing_int([1]) == 2
    assert func.missing_int([]) == 1
    assert func.missing_int([0]) == 1
    assert func.missing_int([2, 1]) == 3
    assert func.missing_int([-1, -2, -3]) == 1
    assert func.missing_int([1, 1]) == 2
    assert func.missing_int([1000, -1]) == 1
    assert func.missing_int([-10, -3, -100, -1000, -239, 1]) == 2
    assert func.missing_int([1, 1]) == 2



回答8:


JavaScript:

let findFirstMissingNumber = ( arr ) => {
  // Sort array and find the index of the lowest positive element.
  let sortedArr = arr.sort( (a,b) => a-b );
  const lowestPositiveIndex = arr.findIndex( (element) => element > 0 );

  // Starting from the lowest positive element
  // check upwards if we have the next integer in the array.
  let i = lowestPositiveIndex;
  while( i < sortedArr.length ) {
    if ( sortedArr[ i + 1 ] !== sortedArr[ i ] + 1 ) {
      return sortedArr[ i ] + 1
    } else {
      i += 1;
    }
  }
}

console.log( findFirstMissingNumber( [3, 4, -1, 1, 1] ) ); // should give 2
console.log( findFirstMissingNumber( [0, 1, 2, 0] ) ); // should give 3



回答9:


Here's another python implementation with o(n) time complexity and o(1) space complexity

def segregate(arr):
    length = len(arr)
    neg_index = length
    for i, value in enumerate(arr):
        if(value < 1 and neg_index == length):
           neg_index = i
        if(neg_index != length and value >= 1):
           temp = arr[i]
           arr[i] = arr[neg_index]
           arr[neg_index] = temp
           neg_index += 1
    return arr[:neg_index]

def missingPositiveNumber(arr):
    arr = segregate(arr)
    length = len(arr)
    for i, value in enumerate(arr):
        if(value - 1 < l):
           arr[abs(value) - 1] = -(abs(arr[abs(value) - 1]))
    for i, value in enumerate(arr):
        if(value > 0):
           return i + 1
    return length + 1


print(missingPositiveNumber([1, -1, 2, 3]))



回答10:


This is in Java. Time complexity f O(N) and space complexity O(1)

private static int minimum_positive_integer(int[] arr) {
        int i = 0;
        int j = arr.length - 1;

        //splitting array
        while (i < j) {
            if (arr[i] > 0) {
                i++;
            }

            if (arr[j] <= 0) {
                j--;
            }

            if (arr[i] <= 0 && arr[j] > 0) {
                int t = arr[i];
                arr[i] = arr[j];
                arr[j] = t;

                i++;
                j--;
            }
        }
        int len_positive = i;

        if (arr[i] > 0) len_positive++;

        for (i = 0; i < len_positive; i++) {
            int abs = Math.abs(arr[i]);
            if (abs <= len_positive) {
                int index = abs - 1;
                arr[index] = -abs;
            }
        }

        for (i = 0; i < len_positive; i++) {
            if(arr[i] > 0) return  i + 1;
        }

        return len_positive + 1;
    }



回答11:


My solution in Python:

def lowest_positive(lista):

result = 0
dict = {}

for i in lista:

    if i <= 0:
        continue

    if i in dict:
        continue
    else:
        dict[i] = i

        if result == 0:
            result = result +1

        if result < i: 
            continue

        result = result +1

        while result in dict:
            result = result +1

return result

Test cases:

lista = [5, 3, 4, -1, 1, 2]
lista = [1,2,3,4,5]
lista = [3, 4, -1, 1]
lista = [2, 3, 4, 1]
lista = [1,0]
lowest_positive(lista)

Please note, I am not considering 0 as positive number

Logic: If number is less than 0, it is rejected. Then the number is checked in dictionary if it exist, if yes, the next number is read else it is added to the dictionary. result is the counter which is incremented one by one. if result is less than the number read in the list, the next number is read else, the counter is increased by one and this result is also checked in dictionary. Dictionary in all will store all the number read in the list, plus any missing positive numbers in between the smallest number read in the list.




回答12:


Drawback of the above approach is it takes extra space for assigning "max_value" which is not the right solution

def missing_positive_integer(my_list):
    max_value = max(my_list)
    my_list = [num for num in range(1,max(my_list)) if num not in my_list]
    if len(my_list) == 0:
        my_list.append(max_value+1)

    return min(my_list)

my_list = [1,2,3,4,5,8,-1,-12,-3,-4,-8]
missing_positive_integer(my_list)



回答13:


I didn't test it in detail, but for sorted array here is how I would approach it, any improvements are welcome. constrains:

  • linear time
  • constant space

    solution:
    start with lowest positive integer (i.e. lpi <- 1)
    while parsing the array, if lpi is already in the array, increment it
    

lpi is now the lowest positive integer not available in the array

simple python function will be as follows:

def find_lpi(arr):
    lpi = 1
    for i in arr:
        if lpi == i:
            lpi += 1
    return lpi

if the array is unsorted, the following could be alternative solution.

first create a binary array, X, of zeroes with length max(arr). For each item in the array mark the index of the X, as 1 return the smallest index with 0

the following is a simple implementation satisfying

  • linear time
  • constant space complexity constraints.

    def find_lpi(arr):
        x = [0 for x in range(max(arr)+1)]
         for i in arr:
             x[i] = 1 
         for i in range(1,len(x)):
             if x[i] ==0:
                 return i
         return len(x)
    



回答14:


public int FindMissing(){
    var list = new int[] { 6, -6, 4, 5 };
    list = list.OrderBy(x => x).ToArray();
    var maxValue = 0;
    for (int i = 0; i < list.Length; i++)
    {
        if (list[i] <= 0)
        {
            continue;
        }
        if (i == list.Length - 1 ||
            list[i] + 1 != list[i + 1])
        {
            maxValue = list[i] + 1;
            break;
        }
    }
    return maxValue;
}
  1. sort the data by ascending order:
  2. for loop the data
    • if value less than equal to 0 then do nothing and skip.
    • check if the current index value plus 1 was equal to next index value
      • if yes, continue with the loop.
      • if no, current index value plus 1 will be the missing positive integer


来源:https://stackoverflow.com/questions/51346136/given-an-array-of-integers-find-the-first-missing-positive-integer-in-linear-ti

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!