​BM53 缺失的第一个正整​

知识点​数组​​​​哈希​

描述

给定一个未排序的整数数组nums,请你找出其中没有出现的最小的正整数

进阶: 空间复杂度 [数组]BM53 缺失的第一个正整数-中等_原地hash,时间复杂度 [数组]BM53 缺失的第一个正整数-中等_原地hash_02

数据范围:-231<=nums[i]<=231-10<=len(nums)<=5*105

示例1

输入:

[1,0,2]

复制返回值:

3

复制

示例2

输入:

[-2,3,4,1,5]

复制返回值:

2

复制

示例3

输入:

[4,5,6,8,9]

复制返回值:

1


题解

遍历一遍,使用hash表存放所有出现过的数字,可以只存1~n+1的值。然后从1到n+1进行遍历,第一个未出现的就是要找 的值。这个时间复杂度满足题目要求,但是空间复杂度为O(N),不满足。

牛客网提供了2种原地hash的解法

原地交换,让nums[i]的值放到其对应的索引上(网友提供)

在原数组原地交换,而我们关心的是正数,所以如果 nums[i] 是正数 > 0 且 < n 且 nums[i] 的位置上不是当前数,就把他放到应该的位置上去(防止死循环,如果原来就在正确的位置上,一直交换)。每个正数 nums[i] 的位置在 nums[i] - 1 上,即 0 的位置上放 1,以此类推(因为数组的大小是 n,那如果出现 n 放在位置 n 上会越界)。最后再遍历一遍数组,如果当前位置上的数 nums[i] 不等于 i + 1,则 i + 1 就是缺少的正整数。

如果遍历结束之后,还没有找到答案,说明所有数都在正确的位置上,是一个连续序列,那么答案就是 n + 1。

    int minNumberDisappeared(vector<int>& nums) {
int n = nums.size();
for(int i=0; i<nums.size(); i++){
while(nums[i]>0 && nums[i] <=n && nums[i] != nums[nums[i]-1]){
swap(nums[i], nums[nums[i]-1]);
}
}
for(int i=0; i<n; i++){
if(nums[i] != i+1)
return i+1;
}
return n+1;
}

原地hash(官方答案)

思路:

前面提到了数组要么缺失[数组]BM53 缺失的第一个正整数-中等_数组_03中的某个数字,要么缺失[数组]BM53 缺失的第一个正整数-中等_原地hash_04,而数组正好有下标[数组]BM53 缺失的第一个正整数-中等_数组_05可以对应数字[数组]BM53 缺失的第一个正整数-中等_数组_03,因此只要数字[数组]BM53 缺失的第一个正整数-中等_数组_03中某个数字出现,我们就可以将对应下标的值做一个标记,最后没有被标记的下标就是缺失的值。

具体做法:

  • step 1:我们可以先遍历数组将所有的负数都修改成n+1。
  • step 2:然后再遍历数组,每当遇到一个元素绝对值不超过n时,则表示这个元素是1~n中出现的元素,我们可以将这个数值对应的下标里的元素改成负数,相当于每个出现过的正整数,我们把与它值相等的下标都指向一个负数,这就是类似哈希表的实现原理的操作。
  • step 3:最后遍历数组的时候碰到的第一个非负数,它的下标就是没有出现的第一个正整数,因为它在之前的过程中没有被修改,说明它这个下标对应的正整数没有出现过。