Find every number from 1 to n that is missing
You're given an array nums of length n where every value lies between 1 and
n, and a value can turn up any number of times. I'd like you to return every number from 1 to
n that doesn't appear in the array, in increasing order.
Try to do it in O(n) time with no extra memory apart from the list you hand back. You're allowed to change the array as you go.
Examples
| Input | Output | Why |
|---|---|---|
nums = [4, 3, 2, 7, 8, 2, 3, 1] | [5, 6] | Eight values and the two that never turn up are 5 and 6. |
nums = [1, 1] | [2] | The 1 is there twice and that leaves no room for the 2. |
nums = [1, 2, 3] | [] | Every number from 1 to 3 is there, so nothing is missing. |
Constraints
- 1 ≤ n ≤ 10⁵
- 1 ≤ nums[i] ≤ n
Hints
Hint 1
With a set you'd put every value in and then count from 1 to n looking for gaps, so ask yourself what the set is actually storing for each number.
Hint 2
All it stores is a yes or a no for each number from 1 to n, and you've already got an array with exactly n slots you're allowed to write on.
Hint 3
Make nums[v − 1] negative whenever you meet v, and when you've gone through the whole array the slots that are still positive tell you which numbers never turned up.
Where people lose marks
- Flipping the sign with nums[j] = −nums[j] on every visit. A value that turns up twice flips its slot back to positive and the number then looks missing, so you want to set it to minus its absolute value.
- Reading nums[i] where you meant |nums[i]|, which goes wrong as soon as an earlier step has flipped the slot you're standing on.
- Leaving the array full of minus signs when the caller still needs it. If that matters you can take the absolute value of every slot in the second pass while you collect the answer.
Dry run on [4,3,2,7,8,2,3,1]
Read it one frame at a time, the way you would trace the code on paper in an interview. Each frame shows the state after the step it describes.
-
Step 1 / 10nums4031227384253617
There are 8 slots and every value is between 1 and 8, so we'll use the sign of slot v − 1 to remember that we've met v.
-
Step 2 / 10nums40i3122-7384253617v4
We meet 4 in slot 0 for the first time, so we make slot 3 negative and it now reads -7.
-
Step 3 / 10nums4031i-22-7384253617v3
We meet 3 in slot 1 for the first time, so we make slot 2 negative and it now reads -2.
-
Step 4 / 10nums40-31-22i-7384253617v2
We meet 2 in slot 2 for the first time, so we make slot 1 negative and it now reads -3.
-
Step 5 / 10nums40-31-22-73i8425-3617v7
We meet 7 in slot 3 for the first time, so we make slot 6 negative and it now reads -3.
-
Step 6 / 10nums40-31-22-7384i25-36-17v8
We meet 8 in slot 4 for the first time, so we make slot 7 negative and it now reads -1.
-
Step 7 / 10nums40-31-22-738425i-36-17v2
We meet 2 again in slot 5, and slot 1 is already negative so it stays as it is.
-
Step 8 / 10nums40-31-22-738425-36i-17v3
We meet 3 again in slot 6, and slot 2 is already negative so it stays as it is.
-
Step 9 / 10nums-40-31-22-738425-36-17iv1
We meet 1 in slot 7 for the first time, so we make slot 0 negative and it now reads -4.
-
Step 10 / 10nums-40-31-22-738425-36-17answer[5, 6]
The slots still positive are 4 and 5, so the missing numbers are [5, 6].
How to think about it
You'll usually see this one called Find All Numbers Disappeared in an Array, and if you've done the
duplicates problem just before it you've already done most of the work. A set would only be keeping one bit for
each number from 1 to n, and since all the values start out positive you can keep that bit in the
sign of slot v − 1.
So you go along the array and for each value you take v = |nums[i]| (the absolute value, because an
earlier step might already have flipped the slot you're reading) and make nums[v − 1] negative if it
isn't already. It doesn't matter how many times a value turns up, since a slot that's already negative just stays
that way.
Then you make one more pass and every slot i that's still positive means the number
i + 1 was never there. That pass goes from left to right, so the list comes out in increasing order
without any sorting, and the whole thing is two passes with nothing extra but the answer.
Complexity. Time O(n) · Space O(1) besides the output
The solution, in six languages
Each one is compiled and run against the test table before it is published. Java is reviewed by hand.
def all_missing(nums):
for x in nums:
j = abs(x) - 1 # an earlier step may have flipped this slot
nums[j] = -abs(nums[j]) # mark v as seen; stays negative on a repeat
return [i + 1 for i, x in enumerate(nums) if x > 0]#include <cstdlib>
#include <vector>
using namespace std;
vector<int> solve(vector<int> nums) {
for (size_t i = 0; i < nums.size(); i++) {
int j = abs(nums[i]) - 1; // an earlier step may have flipped this slot
nums[j] = -abs(nums[j]); // mark v as seen; stays negative on a repeat
}
vector<int> out;
for (size_t i = 0; i < nums.size(); i++)
if (nums[i] > 0) out.push_back(i + 1);
return out;
}import java.util.*;
class Solution {
public int[] allMissing(int[] nums) {
for (int i = 0; i < nums.length; i++) {
int j = Math.abs(nums[i]) - 1; // an earlier step may have flipped this slot
nums[j] = -Math.abs(nums[j]); // mark v as seen; stays negative on a repeat
}
List<Integer> out = new ArrayList<>();
for (int i = 0; i < nums.length; i++) if (nums[i] > 0) out.add(i + 1);
int[] r = new int[out.size()];
for (int i = 0; i < r.length; i++) r[i] = out.get(i);
return r;
}
}function allMissing(nums) {
for (let i = 0; i < nums.length; i++) {
const j = Math.abs(nums[i]) - 1; // an earlier step may have flipped this slot
nums[j] = -Math.abs(nums[j]); // mark v as seen; stays negative on a repeat
}
const out = [];
for (let i = 0; i < nums.length; i++) if (nums[i] > 0) out.push(i + 1);
return out;
}fn solve(nums: &[i32]) -> Vec<i32> {
let mut a = nums.to_vec();
for i in 0..a.len() {
let j = (a[i].abs() - 1) as usize; // an earlier step may have flipped this slot
a[j] = -a[j].abs(); // mark v as seen; stays negative on a repeat
}
(0..a.len()).filter(|&i| a[i] > 0).map(|i| i as i32 + 1).collect()
}import qualified Data.IntSet as S
-- Pure Haskell has no cheap in-place sign flip, so this uses a set of what is present.
-- The O(1)-space version needs a mutable array in ST; the marking idea is the same.
solve :: [Int] -> [Int]
solve xs = [v | v <- [1 .. length xs], not (S.member v present)]
where
present = S.fromList xs
Variants to try
- Return the values that turn up twice as well. (The same marking pass finds them, which is the duplicates problem.)
- You aren't allowed to change the array. (Then constant extra space isn't possible this way, and a set or a sorted copy is the honest answer.)
Next problems
Smallest positive integer that is missing
A hash set solves it in one line and is not allowed. The array can be its own hash table, once you see that only n + 1 answers are possible.
Open the problem → Medium20 minFind every value that appears twice
With a set this is a few lines, and the space limit takes the set away. Since every value is between 1 and n, the sign of nums[v − 1] can remember whether you've met v already.
Open the problem → Easy10 minAre two words anagrams?
Two words are anagrams when every letter occurs equally often in both. One table of counts, added for one word and taken away for the other.
Open the problem →Write a solution and run it against the real test table.
Keep going
Smallest positive integer that is missing
A hash set solves it in one line and is not allowed. The array can be its own hash table, once you see that only n + 1 answers are possible.
Open the problem → Medium20 minFind every value that appears twice
With a set this is a few lines, and the space limit takes the set away. Since every value is between 1 and n, the sign of nums[v − 1] can remember whether you've met v already.
Open the problem → Easy10 minAre two words anagrams?
Two words are anagrams when every letter occurs equally often in both. One table of counts, added for one word and taken away for the other.
Open the problem → Arrays and hashingThe pattern behind it
Trade memory for time: a hash map answers "have I seen this?" in O(1), and most array problems reduce to asking it the right question.
Read the pattern →Preparing for a real process? Quant interview preparation, or book a free 20-minute call.