给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。
示例 1:
输入:nums = [1,2,0] 输出:3
示例 2:
输入:nums = [3,4,-1,1] 输出:2
示例 3:
输入:nums = [7,8,9,11,12] 输出:1
提示:
1 <= nums.length <= 5 * 105-231 <= nums[i] <= 231 - 1- class Solution {
- public int firstMissingPositive(int[] nums) {
- // 过滤无效参数
- if (nums == null || nums.length == 0) {
- return 1;
- }
-
- // l左边都是有效区的数。有效区的所有书都满足nums[i] = i + 1
- int l = 0;
- // r及其r右边的数都是垃圾区。永远不可能加入到有效区的数,就加入垃圾区
- int r = nums.length;
- while (l != r) {
- // 1、如果l位置的数等于l+1,说明这个数是有效区的数,加入有效区
- if (nums[l] == l + 1) {
- // 有效区右扩
- l++;
- // 2、如果l位置的数已经存在在有效区了(nums[l] <= l) 或者 l位置的数大于r 或者在nums[l] - 1已经存在符合有效区规则的nums[l]了,就将l位置的数加入到垃圾区
- } else if (nums[l] <= l || nums[l] > r || nums[nums[l] - 1] == nums[l]) {
- // 将l交换到垃圾区,并且将垃圾区左扩
- swap(nums, l, --r);
- // 如果nums[nums[l] - 1] != nums[l],就将l和nums[l] - 1位置的数交换
- } else {
- swap(nums, l, nums[l] - 1);
- }
- }
-
- // r+1就是缺少的最小的正数
- return r + 1;
- }
-
- public void swap(int[] nums, int i, int j) {
- int temp = nums[i];
- nums[i] = nums[j];
- nums[j] = temp;
- }
- }
假设L来到下标17位置,说明0~16位置属于有效区,有效区内已经存好了1....17依次增加的正整数,那么此时L位置的数,什么情况下才是要被放到垃圾区呢?
1) L位置的数 <= L,说明这个数已经存在于有效区了,要将其放到垃圾区,已经收集过的数字会让最好情况变差,因为会占用空间。
2)如果L位置的数 > R,那么它也是垃圾区的数,因为大小超过R,前面一定有不存在的更小的正整数。很好理解,假设有5个位置,然后放1~10大小的数,其中的数肯定有断层的时候,这个断层中就有最小的没出现过的正整数。
3)假设此时R在31位置,L在17位置。如果L位置上的数是23,我们不能确定他是不是垃圾,因为它小于等于R,后面可能成为有效区的数。这个时候23如果是有效区的数,那么他就应该放在下标22位置,所以将他和下标22位置的数交换:
4)如果此时L位置的数等于L+1,那么这个数就是有效区的,将L右移,将其加入有效区。