这题要求在O(n)时间复杂度,O(1)空间复杂度完成,摆明了只能扫描一次,并且几乎不能用额外的存储结构。
那么我的第一反应思路是用比特位,初始化一个全0的比特位图,然后逐个扫描数组元素,假设数组长度为x,则一次扫描这x元素,如果是碰到正数,则在该数对应的比特位放置1,例如如果是5,则在第5位置1。最后再从低位往高为扫描到第一个0的位置即为first missing positive
我是用的Java,里面有个很好的数据结构BitSet很快就写出来了这个逻辑,并且经过测试也是对的,可是没能通过leetcode OJ, 提示时内存消耗太大!
于是一想,我构建的这个比特位图,虽然计算逻辑很合理,但是有一点不太合理,就是这个比特位图可能非常稀疏,例如1,2,100000000,这样的位图,明明才3个元素,确踏马占了1亿个比特位,而同为3个长度的1,2,3这样的数列,却只需要3位长度的比特位图,这怎么行呢?于是想了一个办法,我就盯着前面例子,即两个长度为3的数列,看看能看出什么门道,仔细盯着想了一分钟,突然想起鸽巢原理的反面,就是说N个位置,你放N-1个球,总会有一个空着,那么如果输入的数列总长度为x,那么可能的first missing positive一定在前x+1个位置上,有了这个逻辑,所有的大于x+1的位置的设置,检查都不必做了,因为都是浪费。
于是得到如下更新版的代码:
public int firstMissingPositive(int[] nums) {
BitSet bs = new BitSet();
int maxBit = 0;
for(int i=0;i<nums.length;i++){
// if its positive intege
if(nums[i]>0 && nums[i]<=nums.length){
bs.set(nums[i]);
if(maxBit<nums[i]){
maxBit = nums[i];
}
}
}
for(int j = 1 ;j<=nums.length;j++){
if(!bs.get(j)){
return j;
}
}
return nums.length+1;
}
很快就通过了OJ,嘿嘿,什么hard模式,不过如此