41. 缺失的第一个正数
困难
给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。
示例 1:
输入:nums = [1,2,0] 输出:3
解释:范围 [1,2] 中的数字都在数组中。
示例 2:
输入:nums = [3,4,-1,1] 输出:2
解释:1 在数组中,但 2 没有。
示例 3:
输入:nums = [7,8,9,11,12] 输出:1
解释:最小的正数 1 没有出现。
提示:
1 <= nums.length <= 105
-231 <= nums[i] <= 231 - 1
思路:直接使用Hash表来记录下每个数, 预处理后再去检查1 ~ n + 1 缺失的数.【时间复杂度O(n) 空间复杂度O(n)】 而题目要求空间复杂度O(1)
解决关键: 数组本身是有两个位置可以记录数据的数据结构,一个是索引下标,另一个就是值. 利用这一点来实现常数级别空间复杂度的解决
这里将每个遍历到的值都将数组的索引做一个标记 (为了不影响这个数本身的价值,将其标为负数, 用到该数时再取绝对值, 但如果这里原来数组的数本来就是负数呢? 这里是要找缺失的第一个正数,负数是没有价值的, 所以我们可以将初始就为负数的数替换为一个特定的数. 为了不影响结果, 这个特定的数是要保证是存在的, 所以我们可以将其替换为 1 ~ n+1 的任何数, 只要在预处理的时候检查他存在, 但题目要求的是需要第一个正数, 故如果选择后面的数会出现漏掉前面较小的数的检查. 所以最小数1是你的不二选择)。 用索引下标表示这个数已经存在, 处理完后只需要找到这个数组的第一个正数, 他的下标就是第一个没有出现的正数。而缺失的数肯定在1 ~ n+1 之间的。如果都为负数则缺失的就是 n+1
class Solution {public int firstMissingPositive(int[] nums) {int n = nums.length;// 检查负数的最小标记是否存在 1boolean std = false;for(int i = 0; i < n; i ++) if(nums[i] == 1) std = true;// 不存在直接返回第一个缺失的正数 1if(!std) return 1;// 预处理,为了不让初始值为负数的数干扰后序检查的结果. 将无意义的负数都标记为已经存在的 1for(int i = 0; i < n; i ++) if(nums[i] <= 0) nums[i] = 1;// 将存在的数全都标负, 数组下标是从0开始的, 所以处理时都进行 -1for(int i = 0; i < n; i ++){int t = Math.abs(nums[i]);// 缺失的数只在1 ~ n+1 之间, 超过范围的其它数不需要处理if(t <= n) nums[t - 1] = -Math.abs(nums[t - 1]);}// 找到第一个正数for(int i = 0; i < n; i ++){// 找到时进行 + 1 恢复if(nums[i] > 0) return i + 1;}return n + 1;}
}