leetcode题解之41. 缺失的第一个正数

Posted 刷题之路1

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了leetcode题解之41. 缺失的第一个正数相关的知识,希望对你有一定的参考价值。

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

 

示例 1:

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

示例 2:

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

示例 3:

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

 

提示:

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

方法 1:索引作为哈希表。

数据预处理

首先我们可以不考虑负数和零,因为不需要考虑。同样可以不考虑大于 n 的数字,
因为首次缺失的正数一定小于或等于 n + 1
缺失的正数为 n + 1 的情况会单独考虑。

技术图片

为了不考虑这些数,又要保证时间复杂度为 O(N)mathcal{O}(N) ,因此
不能将这些元素弹出。我们可以将这些数用 1 替换。

技术图片

为了确保缺失的第一个正数不是 1,先要在这步操作前确定 1 是否存在。

如何实现就地算法

现在我们有一个只包含正数的数组,范围为 1n
现在的问题是在 O(N)mathcal{O}(N) 的时间和常数空间内找出首次缺失的正数。

如果可以使用哈希表,且哈希表的映射是 正数 -> 是否存在 的话,这其实很简单。

技术图片

"脏工作环境" 的解决方法是将一个字符串 hash_str 分配 n 个 0,并且用类似于哈希表的方法,如果在数组中出现数字 i 则将字符串中 hash_str[i] 修改为 1

技术图片

我们不使用这种方法,但是借鉴这种 使用索引作为哈希键值 的想法。

最终的想法是 使用索引作为哈希键 以及 元素的符号作为哈希值 来实现是否存在的检测。

例如,nums[2] 元素的负号意味着数字 2 出现在 nums 中。nums[3]元素的正号表示 3 没有出现在 nums 中。

为了完成此操作,我们遍历一遍数组(该操作在数据预处理使得数组中只有正数的操作后),检查每个元素值 elem 以及将nums[elem] 元素的符号变为符号来表示数字 elem 出现在 nums 中。注意,当数字出现多次时需要保证符号只会变化 1 次。

技术图片

算法

现在可以开始写算法了。

  • 检查 1 是否存在于数组中。如果没有,则已经完成,1 即为答案。
  • 如果 nums = [1],答案即为 2
  • 将负数,零,和大于 n 的数替换为 1
  • 遍历数组。当读到数字 a 时,替换第 a 个元素的符号。
    注意重复元素:只能改变一次符号。由于没有下标 n ,使用下标 0 的元素保存是否存在数字 n
  • 再次遍历数组。返回第一个正数元素的下标。
  • 如果 nums[0] > 0,则返回 n
  • 如果之前的步骤中没有发现 nums 中有正数元素,则返回n + 1

代码

class Solution {
  public int firstMissingPositive(int[] nums) {
    int n = nums.length;
<span class="hljs-comment">// 基本情况</span>
<span class="hljs-keyword">int</span> contains = <span class="hljs-number">0</span>;
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; n; i++)
  <span class="hljs-keyword">if</span> (nums[i] == <span class="hljs-number">1</span>) {
    contains++;
    <span class="hljs-keyword">break</span>;
  }

<span class="hljs-keyword">if</span> (contains == <span class="hljs-number">0</span>)
  <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;

<span class="hljs-comment">// nums = [1]</span>
<span class="hljs-keyword">if</span> (n == <span class="hljs-number">1</span>)
  <span class="hljs-keyword">return</span> <span class="hljs-number">2</span>;

<span class="hljs-comment">// 用 1 替换负数,0,</span>
<span class="hljs-comment">// 和大于 n 的数</span>
<span class="hljs-comment">// 在转换以后,nums 只会包含</span>
<span class="hljs-comment">// 正数</span>
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; n; i++)
  <span class="hljs-keyword">if</span> ((nums[i] &lt;= <span class="hljs-number">0</span>) || (nums[i] &gt; n))
    nums[i] = <span class="hljs-number">1</span>;

<span class="hljs-comment">// 使用索引和数字符号作为检查器</span>
<span class="hljs-comment">// 例如,如果 nums[1] 是负数表示在数组中出现了数字 `1`</span>
<span class="hljs-comment">// 如果 nums[2] 是正数 表示数字 2 没有出现</span>
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; n; i++) {
  <span class="hljs-keyword">int</span> a = Math.abs(nums[i]);
  <span class="hljs-comment">// 如果发现了一个数字 a - 改变第 a 个元素的符号</span>
  <span class="hljs-comment">// 注意重复元素只需操作一次</span>
  <span class="hljs-keyword">if</span> (a == n)
    nums[<span class="hljs-number">0</span>] = - Math.abs(nums[<span class="hljs-number">0</span>]);
  <span class="hljs-keyword">else</span>
    nums[a] = - Math.abs(nums[a]);
}

<span class="hljs-comment">// 现在第一个正数的下标</span>
<span class="hljs-comment">// 就是第一个缺失的数</span>
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">1</span>; i &lt; n; i++) {
  <span class="hljs-keyword">if</span> (nums[i] &gt; <span class="hljs-number">0</span>)
    <span class="hljs-keyword">return</span> i;
}

<span class="hljs-keyword">if</span> (nums[<span class="hljs-number">0</span>] &gt; <span class="hljs-number">0</span>)
  <span class="hljs-keyword">return</span> n;

<span class="hljs-keyword">return</span> n + <span class="hljs-number">1</span>;

}
}


class Solution:
def firstMissingPositive(self, nums):
"""
:type nums: List[int]
:rtype: int
"""

n = len(nums)

    <span class="hljs-comment"># 基本情况</span>
    <span class="hljs-keyword">if</span> <span class="hljs-number">1</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> nums:
        <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>
    
    <span class="hljs-comment"># nums = [1]</span>
    <span class="hljs-keyword">if</span> n == <span class="hljs-number">1</span>:
        <span class="hljs-keyword">return</span> <span class="hljs-number">2</span>
    
    <span class="hljs-comment"># 用 1 替换负数,0,</span>
    <span class="hljs-comment"># 和大于 n 的数</span>
    <span class="hljs-comment"># 在转换以后,nums 只会包含</span>
    <span class="hljs-comment"># 正数</span>
    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(n):
        <span class="hljs-keyword">if</span> nums[i] &lt;= <span class="hljs-number">0</span> <span class="hljs-keyword">or</span> nums[i] &gt; n:
            nums[i] = <span class="hljs-number">1</span>
    
    <span class="hljs-comment"># 使用索引和数字符号作为检查器</span>
    <span class="hljs-comment"># 例如,如果 nums[1] 是负数表示在数组中出现了数字 `1`</span>
    <span class="hljs-comment"># 如果 nums[2] 是正数 表示数字 2 没有出现</span>
    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(n): 
        a = abs(nums[i])
        <span class="hljs-comment"># 如果发现了一个数字 a - 改变第 a 个元素的符号</span>
        <span class="hljs-comment"># 注意重复元素只需操作一次</span>
        <span class="hljs-keyword">if</span> a == n:
            nums[<span class="hljs-number">0</span>] = - abs(nums[<span class="hljs-number">0</span>])
        <span class="hljs-keyword">else</span>:
            nums[a] = - abs(nums[a])
        
    <span class="hljs-comment"># 现在第一个正数的下标</span>
    <span class="hljs-comment"># 就是第一个缺失的数</span>
    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">1</span>, n):
        <span class="hljs-keyword">if</span> nums[i] &gt; <span class="hljs-number">0</span>:
            <span class="hljs-keyword">return</span> i
    
    <span class="hljs-keyword">if</span> nums[<span class="hljs-number">0</span>] &gt; <span class="hljs-number">0</span>:
        <span class="hljs-keyword">return</span> n
        
    <span class="hljs-keyword">return</span> n + <span class="hljs-number">1</span>

复杂性分析

  • 时间复杂度: O(N)O(N) 由于所有的操作一共只会遍历长度为 N 的数组 4 次。
  • 空间复杂度: O(1)O(1) 由于只使用了常数的空间。

https://www.jianshu.com/p/aaa924909239















以上是关于leetcode题解之41. 缺失的第一个正数的主要内容,如果未能解决你的问题,请参考以下文章

leetcode每日一题(2020-06-27):41. 缺失的第一个正数

Leetcode 41 缺失的第一个整数

Leetcode之41.缺失的第一个正数

LeetCode:缺失的第一个正数41

[LeetCode 41.] 缺失的第一个正数

LeetCode 41. 缺失的第一个正数 | Python