First Missing Positive

You are given an unsorted integer array nums, which may contain negative numbers, zeros and repeated values.

Return the smallest positive integer (1, 2, 3, ...) that does not appear anywhere in nums.

Aim for a solution that runs in O(n) time and uses only a constant amount of extra memory. You are allowed to rearrange or overwrite the contents of nums while you work.

Example 1

Input: nums = [5,1,3,-2] Output: 2 Why: 1 and 3 are present but 2 is not, so the answer is 2.

Example 2

Input: nums = [8,2,3,4] Output: 1 Why: 1 itself is missing, so it is the answer no matter which larger values appear.

Example 3

Input: nums = [3,1,2] Output: 4 Why: The array holds exactly 1, 2 and 3, so the first gap is 4.

Constraints

  • 1 <= nums.length <= 105
  • -231 <= nums[i] <= 231 - 1

Pattern Path · Hashing & Counting · problem 14 of 169

With a hash set this is easy. What is the largest value the answer could possibly be for an array of length n?
The answer is always between 1 and n + 1. So values outside 1..n can be ignored completely.
Since only values 1..n matter and there are n slots, you can use the array itself as the set: slot v - 1 can record whether v is present.
Swap each value v in 1..n into slot v - 1 until every slot holds either its own value or junk. Then scan for the first slot i that does not hold i + 1.
Target: O(n) time, O(1) extra (the input array is reused) extra space.

Start simple

Sort the array and walk it looking for the first gap (O(n log n)), or put every value in a hash set and test 1, 2, 3, ... (O(n) time but O(n) extra space).

The key idea

The key observation is a bound: an array of length n can contain at most n distinct positive values, so among 1, 2, ..., n + 1 at least one is missing. The answer is therefore in [1, n + 1], and any value outside 1..n is irrelevant.

That bound means the array itself is big enough to act as a presence table: let slot v - 1 be the "home" of value v. Walk through the array; while the current value v is in range and its home does not already hold v, swap it home. The value that comes back gets the same treatment. The check nums[v - 1] != v also stops endless swapping on duplicates.

Each swap places a value in its home permanently, so there are at most n swaps in total; the nested loop is still O(n) overall. Afterwards, scan from the left: the first index i where nums[i] != i + 1 means i + 1 never appeared. If every slot is correct, the array holds exactly 1..n and the answer is n + 1.

Complexity

  • Time: O(n)
  • Space: O(1) extra (the input array is reused)

Python

class Solution:
    def firstMissingPositive(self, nums):
        n = len(nums)
        # Only 1..n can be the answer's competitors, so try to put each such
        # value v into its "home" slot nums[v - 1] by swapping.
        for i in range(n):
            while 1 <= nums[i] <= n and nums[nums[i] - 1] != nums[i]:
                home = nums[i] - 1
                nums[i], nums[home] = nums[home], nums[i]

        # The first slot that does not hold its own value reveals the gap.
        for i in range(n):
            if nums[i] != i + 1:
                return i + 1
        return n + 1

JavaScript

var firstMissingPositive = function(nums) {
  const n = nums.length;
  // Only 1..n can be the answer's competitors, so try to put each such
  // value v into its "home" slot nums[v - 1] by swapping.
  for (let i = 0; i < n; i++) {
    while (nums[i] >= 1 && nums[i] <= n && nums[nums[i] - 1] !== nums[i]) {
      const home = nums[i] - 1;
      [nums[i], nums[home]] = [nums[home], nums[i]];
    }
  }

  // The first slot that does not hold its own value reveals the gap.
  for (let i = 0; i < n; i++) {
    if (nums[i] !== i + 1) return i + 1;
  }
  return n + 1;
};

What the interviewer is checking

The interviewer wants to see you derive the 1..n+1 bound and then reuse the input as a hash table, while proving the nested swap loop is still linear.

Mistakes to avoid

  • Looping forever on duplicates by swapping a value with an identical copy; check the destination, not just the index.
  • Forgetting the case where 1..n are all present, which must return n + 1.
  • Indexing with values that are 0, negative or larger than n.
  • Swapping in an order that reads the index after it has been overwritten (compute the home index first).

Follow-ups to expect

  • Can you do it without swapping? (Mark presence by negating values.)
  • What if the array is read-only? (Need O(n) space or O(n log n) time.)
  • How would you find the k-th missing positive? (Same placement, count gaps.)

In real software

Allocators that hand out the smallest free ID, port number or file descriptor face the same question, and in-place index marking is how memory-tight systems answer it.

Practice next

Next Post Previous Post
No Comment
Add Comment
comment url