Facebook Pixel

1060. Missing Element in Sorted Array 🔒

Problem Description

You are given an integer array nums that is sorted in ascending order, where all elements are unique. You're also given an integer k.

The task is to find the kth missing number in the sequence, starting from the leftmost (first) element of the array.

A "missing number" is any integer that should appear in the continuous sequence starting from nums[0] but is not present in the array. For example:

  • If nums = [4, 7, 9, 10] and k = 1, the first missing number starting from 4 would be 5 (since 5 is not in the array).
  • If k = 2, the second missing number would be 6.
  • If k = 3, the third missing number would be 8.

The solution uses binary search to efficiently find the kth missing number. The helper function missing(i) calculates how many numbers are missing up to index i by using the formula: nums[i] - nums[0] - i. This works because in a continuous sequence with no missing numbers, the element at index i would be nums[0] + i.

The algorithm first checks if k is greater than the total number of missing elements in the array range. If so, it calculates the answer by extending beyond the last element. Otherwise, it uses binary search to find the position where exactly k missing numbers have occurred, then calculates the kth missing number based on that position.

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

Intuition

The key insight is recognizing that we can calculate how many numbers are missing up to any position in the array without actually listing them out.

Think about it this way: if we have a perfectly continuous sequence starting from nums[0], the element at index i should be nums[0] + i. For example, if nums[0] = 4, then ideally we'd have [4, 5, 6, 7, ...]. But in reality, we might have [4, 7, 9, ...] where some numbers are skipped.

The difference between what we actually have (nums[i]) and what we should have (nums[0] + i) tells us exactly how many numbers are missing up to that point. This gives us the formula: missing(i) = nums[i] - nums[0] - i.

Once we can efficiently count missing numbers at any position, finding the kth missing number becomes a search problem. We need to find where in the array the count of missing numbers transitions from less than k to at least k.

Since the array is sorted and the count of missing numbers is monotonically increasing (we can only have more missing numbers as we go further, never fewer), we can use binary search. We're essentially searching for the leftmost position where we have at least k missing numbers.

The edge case is when k is larger than all the missing numbers within the array range. In this case, the kth missing number must be beyond the last element of the array, and we can calculate it directly by extending from the last element.

Learn more about Binary Search patterns.

Solution Approach

The solution implements a binary search algorithm with a helper function to efficiently find the kth missing number.

Helper Function: The missing(i) function calculates the count of missing numbers up to index i:

  • Formula: nums[i] - nums[0] - i
  • This represents the difference between the actual value at index i and what it should be in a continuous sequence

Main Algorithm Steps:

  1. Check if k exceeds array bounds:

    • Calculate total missing numbers in the array: missing(n - 1)
    • If k > missing(n - 1), the answer lies beyond the array
    • Return: nums[n - 1] + k - missing(n - 1)
    • This adds the remaining missing numbers to the last element
  2. Binary Search Implementation:

    • Initialize pointers: l = 0, r = n - 1
    • Search for the leftmost position where missing(mid) >= k
    • In each iteration:
      • Calculate mid = (l + r) >> 1 (bit shift for division by 2)
      • If missing(mid) >= k: move right pointer to mid
      • Otherwise: move left pointer to mid + 1
  3. Calculate the Result:

    • After binary search, l points to the first index where we have at least k missing numbers
    • The kth missing number is between indices l-1 and l
    • Return: nums[l - 1] + k - missing(l - 1)
    • This formula adds the remaining missing numbers needed to reach k from position l-1

Time Complexity: O(log n) due to binary search Space Complexity: O(1) as we only use a few variables

The elegance of this solution lies in avoiding the enumeration of all missing numbers and instead using mathematical relationships to directly compute the answer.

Ready to land your dream job?

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

Start Evaluator

Example Walkthrough

Let's walk through the solution with nums = [4, 7, 9, 10] and k = 3.

Step 1: Calculate missing numbers at each index Using the formula missing(i) = nums[i] - nums[0] - i:

  • Index 0: missing(0) = 4 - 4 - 0 = 0 (no missing numbers before 4)
  • Index 1: missing(1) = 7 - 4 - 1 = 2 (numbers 5 and 6 are missing)
  • Index 2: missing(2) = 9 - 4 - 2 = 3 (numbers 5, 6, and 8 are missing)
  • Index 3: missing(3) = 10 - 4 - 3 = 3 (same 3 missing numbers)

Step 2: Check if k exceeds array bounds

  • Total missing in array: missing(3) = 3
  • Since k = 3 is not greater than 3, we proceed with binary search

Step 3: Binary Search Looking for the leftmost position where missing(mid) >= k:

Iteration 1:

  • l = 0, r = 3
  • mid = (0 + 3) >> 1 = 1
  • missing(1) = 2, which is less than k = 3
  • Move left pointer: l = 2

Iteration 2:

  • l = 2, r = 3
  • mid = (2 + 3) >> 1 = 2
  • missing(2) = 3, which equals k = 3
  • Move right pointer: r = 2

Iteration 3:

  • l = 2, r = 2, loop ends

Step 4: Calculate the result

  • l = 2, so we look at position l - 1 = 1
  • missing(1) = 2 (we have 2 missing numbers by index 1)
  • We need k - missing(1) = 3 - 2 = 1 more missing number
  • Result: nums[1] + (k - missing(1)) = 7 + 1 = 8

Verification: The missing numbers in order are: 5, 6, 8. The 3rd missing number is indeed 8.

Solution Implementation

1class Solution:
2    def missingElement(self, nums: List[int], k: int) -> int:
3        """
4        Find the k-th missing element in a sorted array.
5      
6        Args:
7            nums: A sorted array of integers
8            k: The k-th missing number to find
9          
10        Returns:
11            The k-th missing integer
12        """
13      
14        def count_missing_before_index(index: int) -> int:
15            """
16            Calculate how many numbers are missing before nums[index].
17          
18            The missing count is the difference between:
19            - The actual range covered (nums[index] - nums[0])
20            - The number of elements present (index)
21            """
22            return nums[index] - nums[0] - index
23      
24        n = len(nums)
25      
26        # Check if k-th missing element is beyond the array's last element
27        total_missing_in_range = count_missing_before_index(n - 1)
28        if k > total_missing_in_range:
29            # The k-th missing element is after the last element
30            # Add the remaining missing count to the last element
31            return nums[n - 1] + k - total_missing_in_range
32      
33        # Binary search to find the position where k-th missing element should be
34        left, right = 0, n - 1
35      
36        while left < right:
37            mid = (left + right) // 2
38          
39            if count_missing_before_index(mid) >= k:
40                # k-th missing element is before or at mid
41                right = mid
42            else:
43                # k-th missing element is after mid
44                left = mid + 1
45      
46        # The k-th missing element is between nums[left-1] and nums[left]
47        # Calculate its exact value by adding the remaining count to nums[left-1]
48        return nums[left - 1] + k - count_missing_before_index(left - 1)
49
1class Solution {
2    /**
3     * Find the k-th missing element in a sorted array.
4     * The k-th missing element is the k-th positive integer that is missing from the array.
5     * 
6     * @param nums A sorted array of distinct integers
7     * @param k The k-th missing element to find (1-indexed)
8     * @return The k-th missing element
9     */
10    public int missingElement(int[] nums, int k) {
11        int n = nums.length;
12      
13        // Check if k-th missing element is beyond the last element of array
14        // If so, calculate directly from the last element
15        if (k > countMissingBeforeIndex(nums, n - 1)) {
16            return nums[n - 1] + k - countMissingBeforeIndex(nums, n - 1);
17        }
18      
19        // Binary search to find the position where k-th missing element should be
20        int left = 0;
21        int right = n - 1;
22      
23        while (left < right) {
24            int mid = (left + right) >> 1;  // Equivalent to (left + right) / 2
25          
26            // If number of missing elements before mid is >= k,
27            // the k-th missing element is in the left half (including mid)
28            if (countMissingBeforeIndex(nums, mid) >= k) {
29                right = mid;
30            } else {
31                // Otherwise, search in the right half
32                left = mid + 1;
33            }
34        }
35      
36        // Calculate the k-th missing element based on the element before left position
37        return nums[left - 1] + k - countMissingBeforeIndex(nums, left - 1);
38    }
39  
40    /**
41     * Calculate the number of missing elements before index i (inclusive).
42     * Missing elements are the positive integers that should exist between nums[0] and nums[i]
43     * but are not present in the array.
44     * 
45     * @param nums The sorted array
46     * @param i The index to check up to (inclusive)
47     * @return The count of missing elements before index i
48     */
49    private int countMissingBeforeIndex(int[] nums, int i) {
50        // Total range from nums[0] to nums[i] is (nums[i] - nums[0])
51        // Actual elements present in this range is (i + 1) including both endpoints
52        // So missing elements = total range - actual elements present
53        return nums[i] - nums[0] - i;
54    }
55}
56
1class Solution {
2public:
3    int missingElement(vector<int>& nums, int k) {
4        // Lambda function to calculate how many numbers are missing before index i
5        // Formula: (actual value at i) - (expected value if no gaps) = missing count
6        // Expected value at index i would be nums[0] + i if there were no gaps
7        auto countMissingBeforeIndex = [&](int index) {
8            return nums[index] - nums[0] - index;
9        };
10      
11        int arraySize = nums.size();
12      
13        // Check if the k-th missing number is beyond the array's range
14        // If so, calculate it directly from the last element
15        if (k > countMissingBeforeIndex(arraySize - 1)) {
16            return nums[arraySize - 1] + k - countMissingBeforeIndex(arraySize - 1);
17        }
18      
19        // Binary search to find the leftmost index where 
20        // the count of missing numbers before it is >= k
21        int left = 0;
22        int right = arraySize - 1;
23      
24        while (left < right) {
25            int mid = (left + right) >> 1;  // Equivalent to (left + right) / 2
26          
27            if (countMissingBeforeIndex(mid) >= k) {
28                // k-th missing number is at or before mid
29                right = mid;
30            } else {
31                // k-th missing number is after mid
32                left = mid + 1;
33            }
34        }
35      
36        // The k-th missing number is between nums[left-1] and nums[left]
37        // Calculate it based on nums[left-1] plus the remaining missing numbers needed
38        return nums[left - 1] + k - countMissingBeforeIndex(left - 1);
39    }
40};
41
1function missingElement(nums: number[], k: number): number {
2    // Helper function to calculate how many numbers are missing before index i
3    // Formula: (actual value at i) - (expected value if no gaps) = missing count
4    // Expected value at index i would be nums[0] + i if there were no gaps
5    const countMissingBeforeIndex = (index: number): number => {
6        return nums[index] - nums[0] - index;
7    };
8  
9    const arraySize = nums.length;
10  
11    // Check if the k-th missing number is beyond the array's range
12    // If so, calculate it directly from the last element
13    if (k > countMissingBeforeIndex(arraySize - 1)) {
14        return nums[arraySize - 1] + k - countMissingBeforeIndex(arraySize - 1);
15    }
16  
17    // Binary search to find the leftmost index where 
18    // the count of missing numbers before it is >= k
19    let left = 0;
20    let right = arraySize - 1;
21  
22    while (left < right) {
23        // Calculate middle index using bit shift (equivalent to Math.floor((left + right) / 2))
24        const mid = (left + right) >> 1;
25      
26        if (countMissingBeforeIndex(mid) >= k) {
27            // k-th missing number is at or before mid
28            right = mid;
29        } else {
30            // k-th missing number is after mid
31            left = mid + 1;
32        }
33    }
34  
35    // The k-th missing number is between nums[left-1] and nums[left]
36    // Calculate it based on nums[left-1] plus the remaining missing numbers needed
37    return nums[left - 1] + k - countMissingBeforeIndex(left - 1);
38}
39

Time and Space Complexity

Time Complexity: O(log n)

The algorithm uses binary search to find the position where the k-th missing element would be. The missing(i) helper function calculates how many numbers are missing up to index i in constant time O(1). The binary search loop runs for O(log n) iterations where n is the length of the input array. Each iteration performs constant time operations including calling missing(), comparisons, and index calculations. Therefore, the overall time complexity is O(log n).

Space Complexity: O(1)

The algorithm uses only a constant amount of extra space. The variables l, r, mid, and n are all integers that occupy constant space. The helper function missing(i) doesn't create any additional data structures and only performs arithmetic operations. No recursive calls are made, so there's no additional stack space usage beyond the function call itself. Therefore, the space complexity is O(1).

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Off-by-One Error in Binary Search Boundary

A frequent mistake is incorrectly handling the binary search boundaries, particularly when determining whether to include mid in the next search range.

Pitfall Example:

# Incorrect: Using left = mid instead of left = mid + 1
while left < right:
    mid = (left + right) // 2
    if count_missing_before_index(mid) >= k:
        right = mid
    else:
        left = mid  # Wrong! Can cause infinite loop

Why it fails: When left and right differ by 1, mid equals left, and if the condition fails, left never advances, causing an infinite loop.

Solution: Always use left = mid + 1 when the condition fails to ensure progression.

2. Incorrect Final Calculation Formula

Another common error is using the wrong index or formula when calculating the final result.

Pitfall Example:

# Incorrect: Using nums[left] instead of nums[left - 1]
return nums[left] + k - count_missing_before_index(left)

Why it fails: After binary search, left points to the first position where we have at least k missing numbers. The k-th missing number lies between nums[left-1] and nums[left], not after nums[left].

Solution: Use nums[left - 1] + k - count_missing_before_index(left - 1) to correctly calculate from the previous position.

3. Not Handling Edge Case When k=1 and left=0

When the first missing number occurs before the first element, accessing nums[left - 1] causes an index error.

Pitfall Example:

# If nums = [5, 7, 9] and k = 1, the first missing is before index 0
# After binary search, left = 0
return nums[left - 1] + k - count_missing_before_index(left - 1)  # IndexError!

Solution: Add a special check for when left == 0:

if left == 0:
    # All k missing numbers come before the first element
    return nums[0] - k
else:
    return nums[left - 1] + k - count_missing_before_index(left - 1)

4. Misunderstanding the Missing Count Formula

Developers sometimes incorrectly calculate the missing count by forgetting to subtract the starting position.

Pitfall Example:

# Incorrect: Not accounting for the starting position
def count_missing_before_index(index):
    return nums[index] - index  # Wrong! Assumes sequence starts at 0

Why it fails: If nums = [4, 7, 9], this would incorrectly calculate missing(0) = 4, when actually no numbers are missing before index 0 (since we start counting from 4).

Solution: Always use nums[index] - nums[0] - index to correctly count missing numbers relative to the starting element.

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

What does the following code do?

1def f(arr1, arr2):
2  i, j = 0, 0
3  new_arr = []
4  while i < len(arr1) and j < len(arr2):
5      if arr1[i] < arr2[j]:
6          new_arr.append(arr1[i])
7          i += 1
8      else:
9          new_arr.append(arr2[j])
10          j += 1
11  new_arr.extend(arr1[i:])
12  new_arr.extend(arr2[j:])
13  return new_arr
14
1public static List<Integer> f(int[] arr1, int[] arr2) {
2  int i = 0, j = 0;
3  List<Integer> newArr = new ArrayList<>();
4
5  while (i < arr1.length && j < arr2.length) {
6      if (arr1[i] < arr2[j]) {
7          newArr.add(arr1[i]);
8          i++;
9      } else {
10          newArr.add(arr2[j]);
11          j++;
12      }
13  }
14
15  while (i < arr1.length) {
16      newArr.add(arr1[i]);
17      i++;
18  }
19
20  while (j < arr2.length) {
21      newArr.add(arr2[j]);
22      j++;
23  }
24
25  return newArr;
26}
27
1function f(arr1, arr2) {
2  let i = 0, j = 0;
3  let newArr = [];
4  
5  while (i < arr1.length && j < arr2.length) {
6      if (arr1[i] < arr2[j]) {
7          newArr.push(arr1[i]);
8          i++;
9      } else {
10          newArr.push(arr2[j]);
11          j++;
12      }
13  }
14  
15  while (i < arr1.length) {
16      newArr.push(arr1[i]);
17      i++;
18  }
19  
20  while (j < arr2.length) {
21      newArr.push(arr2[j]);
22      j++;
23  }
24  
25  return newArr;
26}
27

Recommended Readings

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

Load More