# 41 缺失的第一个正数

# 题目

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

示例 1:

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

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

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

提示:

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

# 题解

  1. 通过一次排序,然后一次遍历通过根据对比数进行比较来寻找最小的数

TIP

执行用时:84 ms, 在所有 JavaScript 提交中击败了 39.55% 的用户

内存消耗:37.4 MB, 在所有 JavaScript 提交中击败了 97.26% 的用户

/**
 * @param {number[]} nums
 * @return {number}
 */
var firstMissingPositive = function(nums) {
    let min = 1 // 最小的数,默认为 1
    if(!nums.length) return min // 如果传空数组,则直接返回 1
    let current = 1 // 用于对比的数,从 1 开始比较
    let isNext = true  // 用来记录是否是数组中没出现过的数
    // 排序,按照从小到大的顺序排列
    nums = nums.sort((a,b)=>a - b)

    for (let i = 0, l = nums.length; i < l; i++) {
        let item = nums[i] // 当前值
        let prev = nums[i-1] || 0  // 上一个值,为了排除重复的值

        // 只有当前的数大于 0 的时候,同时当前数不是重复数
        if (item > 0 && prev !== item) {
            /**
             * 如果相同,则把*对比数 +1*
             * 否则,说明找到了缺失的数
            */
            if (item === current ) {
                current++
            } else {
                min = current
                isNext = false
                break
            }
        }
    }
    
    // 如果 为 true,说明数组中没有最小的数
    if(isNext) {
        /**
         * 这里比较是判断
         * 当数组的最后一个数(也就是排序后的最大数)大于 0 时,则为 最大数 + 1,例:[1, 2] 为 3
         * 否则,直接返回 1 即可,例: [-5] 为 1 
        */
        min = nums[nums.length - 1] > 0 ? nums[nums.length - 1] + 1 : min
    }

    return min 
}
  • 时间复杂度:O(n),一次通过 sort 排序,一次 for 循环。
  • 空间复杂度: O(1),使用常数级别的几个变量。
  • 最好的情况: 数组中前几位的值就出现了最小数,那么 for 循环直接 break 退出循环,节省了剩下遍历的时间。
  • 最坏的情况: 也就是 isNext 为 true 的情况,需要遍历整个数组,数组中没有出现最小的数,那么就是 1 或者 最大数+1。
  1. 排序一次,然后遍历依次对比

TIP

执行用时:92 ms, 在所有 JavaScript 提交中击败了 22.94% 的用户

内存消耗:37.7 MB, 在所有 JavaScript 提交中击败了 90.85% 的用户

/**
 * @param {number[]} nums
 * @return {number}
 */
var firstMissingPositive = function(nums) {
    if(!nums.length) return 1
    let min = 1
    nums = nums.sort((a,b)=>a - b)
    
    for(let i = 0, l = nums.length; i < l; i++) {
        if(min === nums[i]) {
            min++
        }
    }

    return min 
};
  • 时间复杂度:O(n),一次通过 sort 排序,一次 for 循环。
  • 空间复杂度: O(1),使用常数级别的几个变量。
  • 最好的情况
  • 最坏的情况
  1. 分别通过三次遍历来找到值,第一次遍历,把小于等于 0 的数和大于数组长度的数修改为 1,当然修改前还需要判断数组是否包含 1;第二次遍历,把数组里的值当做下标,修改对应的值为负数,重复的值需要先绝对值;第三次遍历寻找值是否大于 0,如果大于 0,说明没有出现过。最后如果都没有,则为数组的长度 +1。

TIP

执行用时:88 ms, 在所有 JavaScript 提交中击败了 30.62% 的用户

内存消耗:37.6 MB, 在所有 JavaScript 提交中击败了 93.60% 的用户

/**
 * @param {number[]} nums
 * @return {number}
 */
var firstMissingPositive = function(nums) {
    const {length: l} = nums
    if(!nums.includes(1)) return 1  // 判断是否有 1,如果没有,则直接返回 1

    for(let i=0;i<l;i++) {
        // 依次把小于等于 0 的数和大于数组长度的数修改为 1
        // 因为值肯定在 1 到  n.length 之间,超过的都不是目标值
        let item = nums[i]
        if(item <=0 || item > nums.length){
            nums[i] = 1
        }
    }

    for(let i=0;i<l;i++) {
        // 依次把数值对应的下标修改为负数
        let item = Math.abs(nums[i])
        nums[item-1] = Math.abs(nums[item-1]) * -1
    }

    for(let i=0;i<l;i++) {
        // 如果发现大于 0 的数,说明对应下标为最小的数
        let item = nums[i]
        if(item > 0) {
            return i+1
        }
    }

    // 如果都没有,则为数组的长度 +1
    return l+1
};
  • 时间复杂度:O(n),三次 for循环
  • 空间复杂度:O(1),常数级别的空间
  • 最好的情况: 当然是数组里没有出现 1,或者最小数在数组的前几位出现,这样遇到了直接返回值并退出函数,节省了剩下遍历的时间。
  • 最坏的情况: 数组里没有出现最小数,值为数组的长度 +1,因为需要遍历所有的值。
最后更新时间: 8/31/2020, 11:06:51 PM