给定一个数组,找出数组缺少的最小的正整数

来源:互联网 发布:大数据如何影响制造业 编辑:程序博客网 时间:2024/05/22 16:07

题目使这样的:请设计一个高效算法,查找数组中未出现的最小正整数。

给定一个整数数组A,请返回数组中未出现的最小正整数。

测试样例:

[-1,2,3,4]

返回1


一看到这个题目我想到的是用另外的一个数组B,长度为A的长度+1,来存储遍历数组A的数的值。 if(A[i] == i+1)  B[i+1] = A[i]。然后遍历数组B,出现空缺的地方即是最小未出现的正整数。但是假如要求额外空间复杂度为O(1),那就没办法了。有的人会想到排序,但是排序最好需要O(nlog2n)的时间复杂度。如果要求时间复杂度为O(n),并且空间复杂度为O(1),那么要怎么做呢?这方法是我在牛客网上看到的。听完之后,感觉特别不可思议,实在是太厉害了。在我的理解里,这是一种正向和逆向结合的思想。从前往后和从后往前不停地进行,理想情况和实际情况实时地更新,最后的临界点必然是两者相等。

在这个算法里,有两个额外变量。一个是match,初始值为0。用来标记从正整数1开始已经连续出现的正整数个数。比如说[3, 5, 2, 1, 6] 出现的连续正整数序列为{1 2 3 },所以match= 3;还有一个变量是maxMatch,初始值是整个数组的长度,因为数组在没遍历之前,无法得知其中的元素,所以看作是最理想的情况:里面的数刚好是1到n。

说了两个变量之后,便是算法的部分了。

首先从第0位置开始,如果A[0]=match+1;那么说明出现了下一个连续的正整数,所以match++;

如果A[0] != mach+1,那么有几种情况。

第一种情况是,A[0]<=match,那么这个值是我们不需要的,因为match之前的正整数已经出现了。所以这时候剩下的数最多能使match的值达到maxMatch-1,因为这个无效的元素,需要占用一个位置。同理如果A[0]>maxMatch,这个值也是不需要的,maxMatch-1;还有就是如果这个数在match和maxMatch的范围之内,我们需要判断这个数应该在的位置上,是否已经有了这个数。比如说A[0]=3,那么我们只需要比较A[2]位置上是否是3,如果是那么这个数也不是我们需要的,这是maxMatch-1;以上几种情况都是遍历的位置上的数是不需要的,那么我们应该把后面的数调到这个位置上,进行重新搜索。

还有一种情况就是 match+1<A[0]<=maxMatch,并且A[0]应该在的位置上的数不是A[0],那么这个数就是我们可能需要的,所以把它放在它应该在的位置。比如说[3, 5, 2, 1, 6],3应该在的位置是2,A[2]上的数字是2,不等于3.所以3应该放在2的位置。这时我们应该交换两个数的位置A[0] <=> A[2];

这样一直搜索下去,最终便是达到临界点,然后结束搜索。最终的结果便是match+1;

public static int firstMissingPosition(int[] nums) { if(null == nums){            throw new NullPointerException("输入的数组为空...");        }else if(0 == nums.length){return 1;}                int match = 0;    int maxMatch = nums.length;        while(match<maxMatch){    if(nums[match]==match+1){    match++;    }else if(nums[match]<=match || nums[match]>maxMatch || nums[nums[match]-1] == nums[match]){    maxMatch--;    nums[match] = nums[maxMatch];    }else {int temp = nums[match];nums[match] = nums[temp-1];nums[temp-1] = temp;}    }   return match+1;    }

代码量很少,但是思路真的是很精妙。不得不为之赞叹!



0 0