数组中未出现的最小正整数

来源:互联网 发布:男人月薪4500 知乎 编辑:程序博客网 时间:2024/03/29 14:23

题目:数组中未出现的最小正整数

 

给定一个无序整型数组arr,找到数组中未出现的最小正整数。要求时间复杂度为O(N),空间复杂度为O(1)。

例如:

arr=[-1,2,3,4]。返回1。

arr=[1,2,3,4]。返回5。

 

分析:

最小正整数是1,所以常规的方法就是在数组中找1,然后是2,依次找下去...。一直找到第一个没有出现的正整数,这个数就是未出现的最小的正整数。但是这样的时间复杂度为O(N*N)。

题目要求时间复杂度为O(N),空间复杂度为O(1)。就是说数组只能从头到尾变遍历一次,而且不能构造新数组copy原数组中的数。

此题目为乐视网2016校园招聘软开笔试题的最后一道题,笔者当时就挂在这道题上,以至于没有面试机会。后来经过一番研究终于把题目做出来了,主要是参考了《程序员代码面试指南》(左程云编写的)中的解析思路,现在分享给大家。

最理想的状况是数组已经排好序了且非负,此时从头到尾遍历数组,找到第一个值不等于下标+1的数,下标+1就是那个未出现的最小正整数。

如下表

0

1

2

3

4

1

2

4

7

8

arr[5] = {1,2,4,7,8}

其中arr[0] = 1 = 0 + 1;

    arr[1] = 2 = 1 + 1;

    arr[2] = 4 != 2 + 1;

故未出现的最小正整数为2 + 1 = 3。

但题目给的是无序整形数组,理想状况发生的概率几乎为零,不能直接用上述方法找出,我们只能把所有可能出现的情况都分析一遍。

(1)  arr为整数1,2,3…N的一个随机排列,那个未出现的最小正整数就是N+1。

(2)  arr中有小于1或者大于N的数出现(我们称之为“不合法”的数),则未出现的最小正整数一定在1到N中间(因为数组一共只有N个数,如果出现不合法的数,则出现的1到N之间的数的个数一定小于N,故一定有没有出现的数)。

基于以上分析,具体实现过程如下:

(1)  先设置两个变量L,R。

L表示已经从1到L已经出现(左边界),L的初值为0。如果一个数字过大(不合法),就会被扔掉,用R表示这个右边界,即大于R的数会被扔掉。R的初值为N,表示从1到R的元素都不会被扔掉,大于R的就会被扔掉。但是这个R的值是变化的,如果L+1到R中有一个元素不合法,那么R--,因为最多只能放下R-1个合法数。

也就是说,1到L上的数已经出现,[L+1,R]区间上的数未出现但可能会出现。

(2)  从左开始遍历数组元素,遍历到L时,若arr[L]= L + 1,由于L表示前面已经出现的正整数,则说明1到L+1均出现,L++;若arr[L]<=L,R--;若arr[L]>R,arr[L] = arr[R-1],R--;

(3)  若arr[arr[L]-1]==arr[L],说明有重复值,,arr[L] = arr[R-1],R--;

(4)  若以上情况均未发生,交换arr[L]与arr[arr[L]-1],继续从L开始遍历。

(5)  最后L=R,返回L+1。

 

 

具体代码实现如下

#include<stdio.h>

 

int missMinNum(int arr[], int n)

{

    int l = 0;

    int r = n;

   int temp;

 

    while(l < r)

    {

        if(arr[l] == l +1)        {

            l++;

        }

        else if(arr[l]> r || arr[l] <= l || arr[arr[l] - 1] == arr[l])//不合法

        {

            arr[l] =arr[--r];

        }

        else//合法但是没有在理想的位置上

        {

           

          temp = arr[l];

          arr[l] =arr[arr[l] - l];

          arr[arr[l] - l] =arr[l];

        }

 

    }//while

 

    return l + 1;

 

}

int main(void)

{

   int a[6] ={1,3,5,-1,4,7};

   int n = 6;

   printf("%d\n",missMinNum(a,n));

   return 0;

 

}

0 0