41. First Missing Positive 缺失的第一個正數

Title

給你一個未排序的整數數組,請你找出其中沒有出現的最小的正整數。

示例 1:

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

示例 2:

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

示例 3:

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

提示:

你的算法的時間複雜度應爲O(n),並且只能使用常數級別的額外空間。

哈希表

Solve

我們可以將數組所有的數放入哈希表,隨後從1開始依次枚舉正整數,並判斷其是否在哈希表中。

如果數組的長度爲N,那麼第一種做法的時間複雜度爲O(N),空間複雜度爲O(N)。

我們爲什麼要使用哈希表?

因爲哈希表是一個可以支持快速查找的數據結構:給定一個元素,可以在O(1)的時間查找該元素是否在哈希表中。

因此,我們可以考慮將給定的數組設計成哈希表的【替代產品】。

實際上,對於長度爲N的數組,其中沒有出現的最小正整數只能在[1, N+1]中,這樣以來,我們將所有在[1, N]範圍內的數放入哈希表,也可以得到最終的答案,而給定的數組恰好長度爲N,這讓我們有了一種將數組設計成哈希表的思路:

我們對數組進行遍歷,對於遍歷到的數 x,如果它在 [1, N] 的範圍內,那麼就將數組中的第 x-1 個位置(注意:數組下標從 0 開始)打上「標記」。在遍歷結束之後,如果所有的位置都被打上了標記,那麼答案是 N+1,否則答案是最小的沒有打上標記的位置加 1。

由於數組中的數沒有任何限制,因此這並不是一件容易的事情。但我們可以繼續利用上面的提到的性質:由於我們只在意 [1, N] 中的數,因此我們可以先對數組進行遍歷,把不在 [1, N] 範圍內的數修改成任意一個大於 N 的數(例如 N+1)。這樣一來,數組中的所有數就都是正數了,因此我們就可以將「標記」表示爲「負號」。算法的流程如下:

  1. 我們將數組中所有小於等於 0 的數修改爲 N+1;
  2. 我們遍歷數組中的每一個數 x,它可能已經被打了標記,因此原本對應的數爲 |x|,其中 | | 爲絕對值符號。如果 ∣x∣∈[1,N],那麼我們給數組中的第 ∣x∣−1 個位置的數添加一個負號。注意如果它已經有負號,不需要重複添加;
  3. 在遍歷完成之後,如果數組中的每一個數都是負數,那麼答案是 N+1,否則答案是第一個正數的位置加 1。

在這裏插入圖片描述

Code

	def firstMissingPositive(self, nums: List[int]) -> int:
		length = len(nums)
		for i in range(length):
			if nums[i] <= 0:
				nums[i] = length + 1
		for i in range(length):
			num = abs(nums[i])
			if num <= length:
				nums[num - 1] = -abs(nums[num - 1])
		for i in range(length):
			if nums[i] > 0:
				return i + 1
		return length + 1

複雜度分析

時間複雜度:O(N),其中 N 是數組的長度。

空間複雜度:O(1)。

置換

Solve

將給定的數組「恢復」成下面的形式:如果數組中包含 x∈[1,N],那麼恢復後,數組的第 x - 1 個元素爲 x。

在恢復後,數組應當有[1, 2, …, N]的形式,但其中有若干個位置上的數是錯誤的,每一個錯誤的位置就代表了一個缺失的正數。

以題目中的示例二 [3, 4, -1, 1] 爲例,恢復後的數組應當爲 [1, -1, 3, 4],我們就可以知道缺失的數爲 2。

那麼我們如何將數組進行恢復呢?我們可以對數組進行一次遍歷,對於遍歷到的數x=nums[i],如果 x∈[1,N],我們就知道 x 應當出現在數組中的 x - 1 的位置,因此交換nums[i] 和 nums[x−1],這樣 x 就出現在了正確的位置。

在完成交換後,新的 nums[i] 可能還在 [1,N] 的範圍內,我們需要繼續進行交換操作,直到 x ∉ [1,N]。

注意到上面的方法可能會陷入死循環。如果 nums[i] 恰好與 nums[x−1] 相等,那麼就會無限交換下去。

因此在這種情況下,x 本就已經出現在了正確的位置,只是數組裏面有多個 x 而已。因此我們就不需要進行交換,而是開始遍歷下一個數。

由於每次的交換操作都會使得某一個數交換到正確的位置,因此交換的次數最多爲 N,整個方法的時間複雜度爲 O(N)。

Code

    def firstMissingPositive(self, nums: List[int]) -> int:
        length = len(nums)
        for i in range(length):
            while 1 <= nums[i] <= length and nums[nums[i] - 1] != nums[i]:
                nums[nums[i] - 1], nums[i] = nums[i], nums[nums[i] - 1]
        for i in range(length):
            if nums[i] != i + 1:
                return i + 1
        return length + 1

複雜度分析

時間複雜度:O(N),其中 N 是數組的長度。

空間複雜度:O(1)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章