LeetCode 41 First Missing Positive (Tag:Array Difficulty:Hard)

246 阅读2分钟

这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战

前言

关于 LeetCode 数组类型题目的相关解法,可见LeetCode 数组类型题目做前必看,分类别解法总结了题目,可以用来单项提高。觉得有帮助的话,记得多多点赞关注哦,感谢!

题目描述

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

请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。  

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

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

示例 3:
输入:nums = [7,8,9,11,12]
输出:1

链接:leetcode-cn.com/problems/fi…

题解

  1. 可以先从简单的方案说起. 可以想到的是利用 hash 的方法, 将 nums 中的元素作为 hash 的下标, 出现在 nums 中的元素, 在 hash 对应下标均为 1. 这样, 将所以的出现过的元素都用 hash 数组标识了, 我们可以从位置 1 开始检查对应位置上的 hash 是否为 1, 一旦发现未出现或者遍历到 hash 数组的尾部了, 那么就可以找出对应的值了. 具体代码见下方, 虽然本方法的时间复杂度为 O(n), 但是空间复杂度为 O(max(nums)), 因此并不满足题目的要求.
/**
 * @param {number[]} nums
 * @return {number}
 */
var firstMissingPositive = function (nums) {
  let hash = [];
  for (let n of nums) {
    if (n >= 0) {
      hash[n] = 1;
    }
  }
  let res = 1;
  let i = 1;
  while (i < hash.length) {
    if (!hash[i]) {
      res = i;
      break;
    }
    i++;
  }
  if (i === hash.length) {
    res = hash.length;
  }
  return res;
};
  1. 方法1中, 我们利用 hash 的方法来达到时间复杂度为 O(n) 的目的, 但是空间复杂度不是常数, 有没有更好的方法来减少空间复杂度呢? 聪明的你可能已经想到了, 我们可以复用 nums 这个数组而不必开辟新的 hash 数组, 怎么做呢.
    首先我们要确定的是, 最终要返回的正整数一定是在 [1, n+1] 的区间内, n 为 nums 的长度, 这是要明确的. 在确定好这一点之后就好办了, 其实只要我们把每个元素放在跟他对应的位置上, 然后从头检查每个位置上的元素和下标是不是对应的就好. 具体来说就是, 比如有元素 1, 就把 1 放在 0 位置上, 元素 5 就把 5 放在 4 位置上, 即数组中元素是下标加一, 这样调整过后, 当位置 i 上的元素不是 i + 1 时, i + 1 就是最后的答案, 如果所有的元素都是对应的, 那就返回 n + 1. 在调整过程中我们可能会遇到小于 1 或者大于 n 的数, 这时候跳过这个位置就好. 具体方法可以见下面的代码. 我们可以称本方法为原地hash, 时间复杂度为 O(n), 空间复杂度为 O(1).
/**
 * @param {number[]} nums
 * @return {number}
 */
var firstMissingPositive = function(nums) {
    const len = nums.length
    
    const swap = (a, b) => {
        let tmp = nums[a]
        nums[a] = nums[b]
        nums[b] = tmp
    }
    
    for (let i = 0; i < len; ++i) {
        // 调整 i 位置的元素, 直到符合要求或者超出范围
        while (nums[i] > 0 && nums[i] < len + 1 && nums[nums[i] - 1] !== nums[i]) {
            swap(i, nums[i] - 1)
        }
    }
    
    for (let i = 0; i < len; ++i) {
        if (nums[i] !== i + 1) {
            return i + 1
        }
    }
    
    return len + 1
};