NeetCode #171LC-41HardArrays & HashingNC 250
← Back to All Problems#171 · #41 · First Missing Positive(缺失的第一个正数)
📌 Problem Statement & Constraints
Given an unsorted integer array
nums, return the smallest positive integer that is not present in nums. Constraints: 1 <= nums.length <= 10^5, -2^31 <= nums[i] <= 2^31 - 1. You must run in O(n) time and use O(1) auxiliary space.💡 Core Algorithmic Approaches
- The answer is always in the range
[1, n + 1], because a permutation of1..nwould make the answern + 1. This bounds the problem usefully. - Cyclic sort / index placement: repeatedly swap so that the value
vends up at indexv - 1, whenever1 <= v <= n. - Use a
whileloop for the swap so that each value keeps moving until it reaches its home. Each successful placement fixes one element permanently, so the total work is O(n). - After the rearrangement, scan for the first index
iwherenums[i] != i + 1. That index gives the answeri + 1; if none is found, the answer isn + 1.
💻 Benchmark Python3 Implementation
class Solution:
def firstMissingPositive(self, nums: List[int]) -> int:
n = len(nums)
for i in range(n):
# keep swapping nums[i] to its home index (nums[i] - 1)
while 1 <= nums[i] <= n and nums[nums[i] - 1] != nums[i]:
target = nums[i] - 1
nums[i], nums[target] = nums[target], nums[i]
# the first slot not holding i+1 reveals the answer
for i in range(n):
if nums[i] != i + 1:
return i + 1
return n + 1⚡ Complexity Deep Dive
⏱️ Time Complexity
O(n) amortised. The nested while looks quadratic, but every swap places one value at its final home and that value never moves again, so there are at most n swaps in total.
💾 Space Complexity
O(1) auxiliary: the rearrangement happens inside nums. A hash-set solution would be O(n) space, which the constraints forbid.
⚠️ Interview Pitfalls & Follow-ups
- Using a set: O(n) time but O(n) space, violating the O(1) requirement. It is the correct baseline to mention before improving.
- Forgetting the upper bound
nums[i] <= n: values larger than n (or non-positive) have no home index and must be left alone, otherwise you index out of range. - Using the condition
nums[i] != i + 1instead ofnums[nums[i]-1] != nums[i]: the correct guard detects when the destination already holds the right value, which is what prevents an infinite swap loop on duplicates. - Using an
ifinstead ofwhile: a single pass would leave some values unmoved; thewhilekeeps pushing until the value settles. - Assuming the array must contain all of
1..n: it need not, which is exactly why the final scan exists.