We have an array of n integers, and every value falls in the range [1, n]. Some numbers in that range appear once, some appear more than once (as duplicates), and some don't appear at all. Our job is to find the ones that are missing.
The constraint that values are in [1, n] drives every approach here. It creates a one-to-one mapping between array indices (0 to n-1) and the values we expect (1 to n). If every number appeared exactly once, the array would be a permutation of [1, n]. Duplicates are why some numbers go missing: each duplicate takes a slot that some other number would have occupied. The problem reduces to finding which numbers from [1, n] never appear.
1 <= n <= 10^5 → With n up to 100,000, O(n^2) would mean 10 billion operations, which is too slow. We need O(n) or O(n log n) at most.1 <= nums[i] <= n → Every value maps to a valid index in the array (value - 1), which lets the array double as its own tracking structure with no extra memory.Put every number into a hash set, then scan 1 to n and report whichever values the set does not contain. The set answers each membership query in O(1), so the whole thing runs in O(n).
This ignores the follow-up's space requirement, but it is the simplest correct solution and a useful baseline to optimize from.
nums into a hash set.Input:
Build the set from nums: {1, 2, 3, 4, 7, 8} (the duplicate 2 and 3 collapse to single entries). Then check each number from 1 to 8 against the set:
The result holds the two values the set never received:
This works but uses O(n) extra space for the set. The next approach removes that cost by storing the "seen" information inside the input array itself, since every value already points to a valid index.
Every value in the array is between 1 and n, so the value v maps to index v - 1. Marking each index that some value points to leaves the unmarked indices corresponding to the missing numbers.
The mark itself goes inside the array, costing no extra space: negate the value at the target position. All values start positive (they are in [1, n]), so a negative value at index i records that the number i + 1 exists somewhere in the array. After one marking pass, a second pass collects every index still holding a positive value. Those positions are the missing numbers.
Two properties keep the marking unambiguous. Because every original value is positive, a sign of "negative" can only mean "some value pointed here," so it is a clean one-bit flag. And taking the absolute value before computing |nums[i]| - 1 recovers the original magnitude even after a position has already been negated, so the value-to-index mapping survives marking.
Duplicates do not corrupt the result. The first copy of a value negates its target index; a later copy finds that index already negative and the if (nums[index] > 0) guard skips it. So each present number flags its index at least once, and exactly the indices no value pointed to stay positive. Index i staying positive means no value equal to i + 1 exists, which is the definition of i + 1 being missing.
index = |nums[i]| - 1. If nums[index] is positive, negate it to mark that index + 1 is present.i where nums[i] is still positive, the number i + 1 is missing. Add it to the result.The negation approach overwrites values with their negatives. The next approach also modifies the array but keeps the values intact, rearranging them into sorted positions instead of flipping signs.
Every value is in [1, n], so each number has a home position: the number v belongs at index v - 1. Once every number sits in its home, a single scan finds the positions holding the wrong number, and those positions correspond to the missing values.
This is the cyclic sort pattern. Each number is swapped to where it belongs in a series of in-place moves. After sorting, index i holds the value i + 1 whenever i + 1 is present. Any index where nums[i] != i + 1 means i + 1 never appeared.
The while loop swaps until either the current number reaches its home or that home already holds the correct value, which happens for duplicates that have nowhere left to go. The index nums[i] - 1 is always valid: values stay in [1, n] throughout, so the subscript stays in [0, n-1] and the swaps never go out of bounds.
Even though a while loop sits inside the for loop, the work is linear. Every swap moves at least one number into its final home, and once a number is home it never moves again. The array has n homes, so the total number of swaps over the whole run is at most n. Add the two linear scans and the algorithm is O(n).
i, repeatedly swap nums[i] into its correct position (nums[i] - 1) until the current element is already in the right place or the target position already holds the correct value (duplicate).i, if nums[i] != i + 1, then i + 1 is a missing number.