---
# System prepended metadata

title: 41. First Missing Positive
tags: [leetcode, hash table]

---

# 資訊

:::info
- Question: 41. First Missing Positive
- From: Leetcode Daily Challenge 2024.03.26
- Difficulty: Hard
:::

---

# 目錄

:::info
[TOC]
:::

---

# 題目

Given an unsorted integer array `nums`. Return the smallest positive integer that is not present in `nums`.

You must implement an algorithm that runs in $O(n)$ time and uses $O(1)$ auxiliary space.

> Example 1:
:::success
- Input: `nums = [1,2,0]`
- Output: 3
- Explanation: The numbers in the range `[1,2]` are all in the array.
:::

> Example 2:
:::success
- Input: `nums = [3,4,-1,1]`
- Output: 2
- Explanation: 1 is in the array but 2 is missing.
:::

> Example 3:
:::success
- Input: `nums = [7,8,9,11,12]`
- Output: 1
- Explanation: The smallest positive integer 1 is missing.
:::

> Constraints:
:::success
- 1 <= `nums.length` <= $10^5$
- $-2^{31}$ <= `nums[i`] <= $2^{31} - 1$
:::

---

# 解法

## 概念

久違了，在最忙的幾天給我塞 hard，真有你的 leetcode！

這題感覺跟昨天很像，一樣是透過 mark 成負的來當作 flag 去做後續判斷

因為題目說要找 first missing positive，當題目給大小為 `n` 的 array 時，可以猜到解答肯定是 `[1:n+1]` 這段區間，而 `n+1` 會出現在當 array 剛好就是 `[1, 2, ..., n]` 的時候

所以在一開始我把整條 array traverse 過一次，把在我關心的元素 (aka `[1:n]`) 以外全部置換成 `n+1`，其實只是要換成一個不會在 `[1:n]` 之中，等等可以判斷的數字即可

接著重新 traverse 過這條 array，從第零個開始，我去取該位置的數值，這時候得到的結果應該就會是 [1:n+1]，而 `n+1` 正好就是那些我不想關心的元素，可以直接忽略（第 12 - 13 行），再來第 14 行是要把該數值變成 0-index，最後就把對應 index 的數值改成負的（第 15 - 16 行），代表該 index + 1 是出現在 `nums` 過的，而為什麼第 11 行要用絕對值取值也是這個原因

最後第 18 - 21 行就是在找第一個不為負的 index 是誰，其加一就會是第一個沒出現在 `nums` 的正數

如果都沒有，代表 `nums = [1:n]`，那就是要回傳 `n+1`（第 23 行）

## 程式碼

```python=
class Solution:
    def firstMissingPositive(self, nums: List[int]) -> int:
        # Reform nums: each element in range [1:n+1]
        n = len(nums)
        for i in range(n):
            if nums[i] <= 0 or nums[i] > n: # Those element I do not care
                nums[i] = n + 1

        # Traverse nums and mark all existing numbers
        for i in range(n):
            num = abs( nums[i] )
            if num > n: # Those element I do not care
                continue
            num -= 1    # To fit the 0-index
            if nums[num] > 0:   # Those element I do care ([1:n])
                nums[num] = -1 * nums[num]  # Mark negative as the (index + 1) "in" nums

        # Find the first missing positive
        for i in range(n):
            if nums[i] > 0:    # Positive means that (index + 1) is not in nums
                return i+1

        return n+1              # [1:n] are all in nums, the first missing positive is n+1
```

---

# 複雜度

## 時間複雜度

首先排除我不關心的 element 花了一次 $O(n)$，traverse 所有 element 並 mark 成負的那時候又花了一次 $O(n)$，最後尋找 first missing positive 也花了一次 $O(n)$，整體是 $O(3n) = O(n)$

題目的 hint 說「記得 $O(2n) = O(n)$」，說不定其他人比較快是因為這樣？

![TimeComplexity20240326](https://hackmd.io/_uploads/S1YrSsJyC.png =80%x)

## 空間複雜度

大多數的空間需求都在 `nums` 裡面處理完，所以跟題目要求一樣是 $O(1)$

![SpaceComplexity20240326](https://hackmd.io/_uploads/B1CISok10.png =80%x)
