Facebook Pixel

448. Find All Numbers Disappeared in an Array

EasyArrayHash Table
Leetcode Link

Problem Description

You are given an array nums containing n integers. Each integer in the array is within the range [1, n], meaning every element has a value between 1 and n (inclusive).

Your task is to find all the integers from 1 to n that are missing from the array nums.

For example:

  • If nums = [4, 3, 2, 7, 8, 2, 3, 1], the length is 8, so we need to check which numbers from 1 to 8 are missing
  • The numbers present are: 1, 2, 3, 4, 7, 8
  • The missing numbers are: 5, 6
  • Therefore, return [5, 6]

The solution uses a set to track which numbers appear in the array. It converts nums to a set s for O(1) lookup time. Then it iterates through all numbers from 1 to n (where n is the length of the array) and collects those that don't exist in the set using a list comprehension: [x for x in range(1, len(nums) + 1) if x not in s].

This approach has O(n) time complexity for creating the set and checking membership, and O(n) space complexity for storing the set.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

When we need to find missing elements from a sequence, the key insight is that we know exactly what the complete sequence should look like - all integers from 1 to n. Since we have the expected complete set and the actual incomplete set, we can find the difference between them.

The most straightforward way to think about this is: "Check each number from 1 to n and see if it exists in our array." However, if we directly search for each number in the original array, we'd have O(n²) time complexity because for each of the n numbers, we'd potentially scan through the entire array.

This leads us to optimize the lookup operation. By converting the array into a set first, we transform the "is this number in the array?" check from O(n) to O(1). Sets are perfect for this because:

  1. They automatically handle duplicates (which might exist in the input array)
  2. They provide constant-time membership testing

Once we have the set, we can efficiently iterate through all possible values [1, 2, 3, ..., n] and collect only those that aren't in our set. The list comprehension [x for x in range(1, len(nums) + 1) if x not in s] elegantly expresses this logic: generate all numbers from 1 to n, but keep only those not found in the set.

The beauty of this approach is its simplicity - we're essentially asking "which numbers from the complete sequence are not in my given sequence?" and using a set to make that comparison efficient.

Solution Approach

The implementation follows a set-based approach to efficiently identify missing numbers:

Step 1: Convert the array to a set

s = set(nums)

This step creates a set from the input array nums. The set automatically:

  • Removes any duplicate values that might exist in the original array
  • Provides O(1) average-case lookup time for membership testing
  • Takes O(n) time to construct where n is the length of the array

Step 2: Generate and filter the complete range

return [x for x in range(1, len(nums) + 1) if x not in s]

This list comprehension performs three operations:

  1. range(1, len(nums) + 1) generates all integers from 1 to n (inclusive), where n is the length of the input array
  2. For each number x in this range, it checks if x not in s - this is where the set's O(1) lookup shines
  3. Collects all numbers that fail the membership test (i.e., the missing numbers) into a result list

Time Complexity Analysis:

  • Creating the set: O(n)
  • Iterating through range 1 to n: O(n)
  • Checking membership for each number: O(1) per check, O(n) total
  • Overall: O(n)

Space Complexity Analysis:

  • The set s stores at most n unique elements: O(n)
  • The output list stores the missing numbers: O(n) in worst case (when input array is empty or contains values outside [1,n])
  • Overall: O(n)

The elegance of this solution lies in transforming a potentially quadratic search problem into a linear one through the strategic use of a set data structure.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a small example to illustrate the solution approach.

Example Input: nums = [3, 1, 3]

Since the array has length 3, we need to find which numbers from 1 to 3 are missing.

Step 1: Convert array to set

nums = [3, 1, 3]
s = set(nums) = {1, 3}

Notice how the duplicate 3 is automatically removed. Our set now contains only the unique values that appear in the array.

Step 2: Check each number from 1 to n

We need to check numbers 1, 2, and 3 (since n = 3):

range(1, len(nums) + 1) = range(1, 4) = [1, 2, 3]

Now we check each number:

  • Is 1 in set {1, 3}? Yes → Don't include in result
  • Is 2 in set {1, 3}? No → Include in result
  • Is 3 in set {1, 3}? Yes → Don't include in result

Step 3: Build the result

The list comprehension [x for x in range(1, 4) if x not in s] evaluates as:

  • x = 1: 1 not in {1, 3} is False → skip
  • x = 2: 2 not in {1, 3} is True → add 2 to result
  • x = 3: 3 not in {1, 3} is False → skip

Final Result: [2]

The number 2 is the only number from 1 to 3 that doesn't appear in our original array, which is correct since our input only contained 1 and 3.

Solution Implementation

1class Solution:
2    def findDisappearedNumbers(self, nums: List[int]) -> List[int]:
3        """
4        Find all numbers in range [1, n] that don't appear in the array.
5      
6        Args:
7            nums: List of integers where n is the length of the array
8      
9        Returns:
10            List of integers that are missing from the range [1, n]
11        """
12        # Convert the input list to a set for O(1) lookup time
13        numbers_present = set(nums)
14      
15        # Build result list by checking each number in range [1, n]
16        # If a number is not in the set, it's missing from the original array
17        missing_numbers = [
18            number 
19            for number in range(1, len(nums) + 1) 
20            if number not in numbers_present
21        ]
22      
23        return missing_numbers
24
1class Solution {
2    /**
3     * Finds all numbers in range [1, n] that do not appear in the array.
4     * 
5     * @param nums Input array containing integers in range [1, n]
6     * @return List of missing numbers from the range [1, n]
7     */
8    public List<Integer> findDisappearedNumbers(int[] nums) {
9        int arrayLength = nums.length;
10      
11        // Create a boolean array to track which numbers are present
12        // Index 0 is unused, indices 1 to n represent numbers 1 to n
13        boolean[] isPresent = new boolean[arrayLength + 1];
14      
15        // Mark numbers that appear in the input array
16        for (int number : nums) {
17            isPresent[number] = true;
18        }
19      
20        // Create result list to store missing numbers
21        List<Integer> missingNumbers = new ArrayList<>();
22      
23        // Check each number from 1 to n
24        // Add to result if not marked as present
25        for (int i = 1; i <= arrayLength; i++) {
26            if (!isPresent[i]) {
27                missingNumbers.add(i);
28            }
29        }
30      
31        return missingNumbers;
32    }
33}
34
1class Solution {
2public:
3    vector<int> findDisappearedNumbers(vector<int>& nums) {
4        int n = nums.size();
5      
6        // Create a boolean array to track which numbers appear in the input
7        // Index represents the number (1 to n), value indicates presence
8        vector<bool> seen(n + 1, false);
9      
10        // Mark all numbers that appear in the input array
11        for (int num : nums) {
12            seen[num] = true;
13        }
14      
15        // Collect all numbers from 1 to n that were not seen
16        vector<int> result;
17        for (int i = 1; i <= n; ++i) {
18            if (!seen[i]) {
19                result.push_back(i);
20            }
21        }
22      
23        return result;
24    }
25};
26
1/**
2 * Finds all numbers from 1 to n that are missing from the input array
3 * @param nums - Array of integers where each integer is in range [1, n]
4 * @returns Array of integers that are missing from the input array
5 */
6function findDisappearedNumbers(nums: number[]): number[] {
7    // Get the length of the input array
8    const arrayLength: number = nums.length;
9  
10    // Create a boolean array to track which numbers appear in the input
11    // Index 0 is unused since we're tracking numbers from 1 to n
12    const numberPresent: boolean[] = new Array(arrayLength + 1).fill(false);
13  
14    // Mark each number that appears in the input array as present
15    for (const number of nums) {
16        numberPresent[number] = true;
17    }
18  
19    // Collect all numbers that are not present
20    const missingNumbers: number[] = [];
21  
22    // Check each number from 1 to n
23    for (let i = 1; i <= arrayLength; i++) {
24        // If the number is not present, add it to the result
25        if (!numberPresent[i]) {
26            missingNumbers.push(i);
27        }
28    }
29  
30    return missingNumbers;
31}
32

Time and Space Complexity

Time Complexity: O(n)

  • Creating a set from the list nums takes O(n) time, where n is the length of the input array
  • The list comprehension iterates through numbers from 1 to n, which is O(n) iterations
  • For each iteration, checking membership in a set (x not in s) is O(1) on average
  • Total time complexity: O(n) + O(n) * O(1) = O(n)

Space Complexity: O(n)

  • The set s stores unique elements from nums, which in the worst case can be all n elements, requiring O(n) space
  • The output list can contain up to n elements in the worst case (when all numbers are missing), requiring O(n) space
  • However, since the output space is typically not counted in space complexity analysis for the algorithm itself, the auxiliary space complexity is O(n) for the set

Common Pitfalls

Pitfall 1: Assuming the array contains only valid numbers in range [1, n]

The Problem: A common mistake is assuming that all numbers in the input array are guaranteed to be within the range [1, n]. If the array contains numbers outside this range (like 0, negative numbers, or numbers greater than n), the current solution will still work correctly for finding missing numbers, but developers might write optimizations or alternative solutions that break with invalid inputs.

Example of the issue:

# If someone tries to optimize by using array indexing directly:
def findDisappearedNumbers_buggy(nums):
    n = len(nums)
    # Mark presence by negating values at corresponding indices
    for num in nums:
        index = abs(num) - 1  # This will crash if num is 0 or > n
        if index < n:  # Without this check, index out of bounds error
            nums[index] = -abs(nums[index])
  
    # Collect indices where values are still positive
    return [i + 1 for i in range(n) if nums[i] > 0]

Solution: Always validate input assumptions or add defensive checks:

def findDisappearedNumbers(nums):
    n = len(nums)
    numbers_present = set()
  
    # Only add numbers that are in valid range
    for num in nums:
        if 1 <= num <= n:
            numbers_present.add(num)
  
    return [i for i in range(1, n + 1) if i not in numbers_present]

Pitfall 2: Memory concerns with very large arrays

The Problem: While the O(n) space complexity is optimal for this problem's requirements, creating a set that duplicates most of the array's content can cause memory issues with extremely large datasets. Developers might not consider the memory footprint when n is in the millions.

Solution: For memory-critical applications, consider an in-place marking approach that modifies the original array:

def findDisappearedNumbers_inplace(nums):
    # Mark presence by making values at corresponding indices negative
    for num in nums:
        index = abs(num) - 1
        if index < len(nums):
            nums[index] = -abs(nums[index])
  
    # Indices with positive values represent missing numbers
    result = []
    for i in range(len(nums)):
        if nums[i] > 0:
            result.append(i + 1)
  
    # Restore original array if needed
    for i in range(len(nums)):
        nums[i] = abs(nums[i])
  
    return result

This approach uses O(1) extra space (excluding the output array) by cleverly using the sign of array elements as markers.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What's the output of running the following function using input [30, 20, 10, 100, 33, 12]?

1def fun(arr: List[int]) -> List[int]:
2    import heapq
3    heapq.heapify(arr)
4    res = []
5    for i in range(3):
6        res.append(heapq.heappop(arr))
7    return res
8
1public static int[] fun(int[] arr) {
2    int[] res = new int[3];
3    PriorityQueue<Integer> heap = new PriorityQueue<>();
4    for (int i = 0; i < arr.length; i++) {
5        heap.add(arr[i]);
6    }
7    for (int i = 0; i < 3; i++) {
8        res[i] = heap.poll();
9    }
10    return res;
11}
12
1class HeapItem {
2    constructor(item, priority = item) {
3        this.item = item;
4        this.priority = priority;
5    }
6}
7
8class MinHeap {
9    constructor() {
10        this.heap = [];
11    }
12
13    push(node) {
14        // insert the new node at the end of the heap array
15        this.heap.push(node);
16        // find the correct position for the new node
17        this.bubble_up();
18    }
19
20    bubble_up() {
21        let index = this.heap.length - 1;
22
23        while (index > 0) {
24            const element = this.heap[index];
25            const parentIndex = Math.floor((index - 1) / 2);
26            const parent = this.heap[parentIndex];
27
28            if (parent.priority <= element.priority) break;
29            // if the parent is bigger than the child then swap the parent and child
30            this.heap[index] = parent;
31            this.heap[parentIndex] = element;
32            index = parentIndex;
33        }
34    }
35
36    pop() {
37        const min = this.heap[0];
38        this.heap[0] = this.heap[this.size() - 1];
39        this.heap.pop();
40        this.bubble_down();
41        return min;
42    }
43
44    bubble_down() {
45        let index = 0;
46        let min = index;
47        const n = this.heap.length;
48
49        while (index < n) {
50            const left = 2 * index + 1;
51            const right = left + 1;
52
53            if (left < n && this.heap[left].priority < this.heap[min].priority) {
54                min = left;
55            }
56            if (right < n && this.heap[right].priority < this.heap[min].priority) {
57                min = right;
58            }
59            if (min === index) break;
60            [this.heap[min], this.heap[index]] = [this.heap[index], this.heap[min]];
61            index = min;
62        }
63    }
64
65    peek() {
66        return this.heap[0];
67    }
68
69    size() {
70        return this.heap.length;
71    }
72}
73
74function fun(arr) {
75    const heap = new MinHeap();
76    for (const x of arr) {
77        heap.push(new HeapItem(x));
78    }
79    const res = [];
80    for (let i = 0; i < 3; i++) {
81        res.push(heap.pop().item);
82    }
83    return res;
84}
85

Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More