41. 缺失的第一个正数

一、题目

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

示例 1:

输入: [1,2,0]
输出: 3

示例 2:

输入: [3,4,-1,1]
输出: 2

示例 3:

输入: [7,8,9,11,12]
输出: 1

提示:

你的算法的时间复杂度应为O(n),并且只能使用常数级别的额外空间。

二、题解

法1

  • 使用nums数组自身作为存储空间进行数字统计,且时间O(n),具体方法参考448. 找到所有数组中消失的数字

    • 正数——出现0次的数——缺少的数
    • 负数——出现次数大于1的数——拥有的数
  • nums数组中的干扰项

    • 负数,由于时负数是由含义的,因此如果初始就为负数,会将索引+1统计成拥有的数
    • 大于nums.length的数,由于是将内容作为索引,数组本身的长度最多存从1~n的数
    • 0既不是正数也不是负数,统计不到
    • 因此将这些数都初始化为1——1~n的数都是可以的,只是其他数要多次进行判断
    • 因为这些数的值对统计结果并没有影响,如[1,5,0]-->2 初始化后[1,1,1]--->2
  • 但如果nums里初始没有1,初始化为1,1就会被统计为拥有,所以在初始化前需要先判断有没有1,没有1的情况下,一定缺少1,而最小正整数就是1,直接输出

  • 特殊情况

    • 数组长度为1,[1]---->2,[其余数]---->缺1都输出1
    • 不缺数 [1,2,3]---->4,而存储后nums为[-1, -2, -3]---->1,因此要单独处理
  • 图解——以[1,-2,10,2,-7]---->3为例

    • 统计的数字 1 2 3 4 5
      nums的索引 0 1 2 3 4
      nums的元素 1 -2 10 2 -7
      含1,初始化n=5 1 1 1 2 1
      第一轮 -1
      第二轮 ---
      第三轮 ---
      第四轮 -1
      第五轮 ---
      存储结果 -1 -1 1 2 1
    • 第一个正数的索引+1——3

class Solution {
    public int firstMissingPositive(int[] nums) {

        int result = 0;
        boolean have1 = false;
        int sum = 0;

        //特殊情况——数组长度为1
        if (nums.length==1){
            return nums[0]==1 ? 2 : 1;
        }

        //检查是否有1
        for (int i = 0; i < nums.length; i++) {
            if (nums[i]==1){
                have1 = true;
            }
        }

        //没有1的情况下一定输出1
        if (!have1) {
            return 1;
        }

        //确定有1的情况下,将干扰项都初始化为1
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
            if (nums[i]<1 || nums[i]>nums.length) {
                nums[i] = 1;
            }
        }

        //特殊情况——不缺数
        if (sum==(nums.length+1)*nums.length/2){
            //和与正确总和相同如[1,3,2]
            return nums.length+1;
        }

        //统计出现次数
        for (int i = 0; i < nums.length; i++) {
            //正数——出现0次的数——缺少的数
            //负数——出现次数大于1的数——拥有的数
            if (nums[Math.abs(nums[i])-1]>0) {
                nums[Math.abs(nums[i])-1] *= -1;
            }
        }

        //第一个正数的索引+1就是要找的数
        for (int i = 0; i < nums.length; i++) {
            if (nums[i]>0) {
                return i+1;
            }
        }

        return result;
    }
}
  • 时间复杂度O(n)

  • 空间复杂度O(1)

  • 执行用时:1 ms, 在所有 Java 提交中击败了87.34%的用户

  • 内存消耗:36.3 MB, 在所有 Java 提交中击败了91.55%的用户

改进

  • 由于特殊情况都列出了,因此当其余可能性都排除了,就只剩下不缺数的可能,因此不用sum处理
class Solution {
    public int firstMissingPositive(int[] nums) {

        int result = 0;
        boolean have1 = false;

        if (nums.length==1){
            return nums[0]==1 ? 2 : 1;
        }
        for (int i = 0; i < nums.length; i++) {
            if (nums[i]==1){
                have1 = true;
            }
        }
        if (!have1) {
            return 1;
        }
        for (int i = 0; i < nums.length; i++) {
            if (nums[i]<1 || nums[i]>nums.length) {
                nums[i] = 1;
            }
        }
        for (int i = 0; i < nums.length; i++) {
            if (nums[Math.abs(nums[i])-1]>0) {
                nums[Math.abs(nums[i])-1] *= -1;
            }
        }
        for (int i = 0; i < nums.length; i++) {
            if (nums[i]>0) {
                return i+1;
            }
        }
        //最后一种情况——不缺数
        return nums.length+1;
    }
}
  • 执行用时:1 ms, 在所有 Java 提交中击败了87.34%的用户
  • 内存消耗:36.2 MB, 在所有 Java 提交中击败了92.80%的用户
posted @ 2020-10-24 19:52  球球z  阅读(92)  评论(0)    收藏  举报