Binary Search - DSA: Kth Missing Positive Number
1. Problem Statement & Overview
Given an increasing array of positive integers and an integer , our task is to find the -th missing positive integer from the sequence starting at .
- Input: A sorted array of positive integers (e.g.,
[2, 3, 4, 7, 11]) and an integer (e.g., ). - Output: The -th missing positive number (e.g., for the example above, the missing numbers are
1, 5, 6, 8, 9, 10, ..., and the -th missing number is9).
2. Real-Life Mental Model & Analogy
The Hostel Room Assignment Analogy
Imagine a row of hostel rooms numbered sequentially starting from :
Room Numbers: [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7 ] [ 8 ] [ 9 ] [ 10 ] [ 11 ]
Occupancy: X Occup. Occup. Occup. X X Occup. X X X Occup.
- Some rooms are occupied (represented by the numbers present in our array).
- Other rooms are vacant (missing numbers).
- If you are asked to walk down the hallway and find the -th vacant room, you count the empty doors one by one until you hit your target.
- The Brute Force approach physically walks through every single room. The Binary Search approach skips blocks of rooms by checking how many vacancies have accumulated up to a certain point.
3. Naive Approach: The Brute Force Strategy
How It Works
We can iterate through natural numbers starting from or iterate through the array while keeping track of how many numbers are missing relative to our target .
Alternatively, as demonstrated in the lecture, we check each element in the array: if the array element is less than or equal to , it means this number occupies a spot that would otherwise be a missing candidate. Therefore, we increment to shift our target forward.
Brute Force Code Implementation (C++)
int findKthPositive(vector<int>& arr, int k) {
for (int i = 0; i < arr.size(); i++) {
if (arr[i] <= k) {
k++; // This element takes up a slot, so our target shifts forward
} else {
break; // We have exceeded our target threshold
}
}
return k;
}
Complexity Analysis
- Time Complexity: where is the number of elements in the array.
- Space Complexity: auxiliary space.
4. Why We Need Binary Search & Intuition Breakdown
When an array is sorted and increasing, and we need a performance upgrade from , Binary Search is the natural choice.
However, two classic binary search techniques fail here:
- Standard Binary Search: Fails because the target number is not present in the array.
- Binary Search on Answers: Typically used for optimization problems with monotonic ranges (minimum/maximum bounds), which doesn't directly map here since we are searching for an index bracket where the missing count transitions past .
The Core Insight: Counting Missing Numbers
At any index mid, how many numbers are missing strictly before or at arr[mid]?
- Ideal Value at Index: In a pristine, uninterrupted sequence starting at , the value at index
midshould bemid + 1(assuming 0-based indexing). - Actual Value at Index:
arr[mid]. - Formula for Missing Numbers:
Let's test this formula on arr = [2, 3, 4, 7, 11] with :
- At index
0(val2): missing = - At index
1(val3): missing = - At index
2(val4): missing = - At index
3(val7): missing = - At index
4(val11): missing =
We are looking for . Notice that the missing count transitions from (at index ) to (at index ). Therefore, our answer lies between index and index (or more specifically, right after high pointer settles).
5. Step-by-Step Binary Search Evolution
We set up our search space:
low = 0high = n - 1
Low (0) High (n-1)
| |
[ 2, 3, 4, 7, 11 ] (K = 5)
Execution Trace:
- Iteration 1:
mid = (0 + 4) / 2 = 2.arr[2] = 4. Missing = .- Since missing () (), our target is further right. Eliminate left half:
low = mid + 1().
- Iteration 2:
mid = (3 + 4) / 2 = 3.arr[3] = 7. Missing = .- Since missing () (), eliminate left half:
low = mid + 1().
- Iteration 3:
mid = (4 + 4) / 2 = 4.arr[4] = 11. Missing = .- Since missing () (), eliminate right half:
high = mid - 1().
At the end of the loop, low points to index (value ) and high points to index (value ). Notice the opposite polarity:
highstopped where missing count ().lowstopped where missing count ().
6. Deriving the O(1) Mathematical Formula for the Answer
When binary search terminates, high and low cross paths (low = high + 1). How do we compute the exact -th missing number without relying directly on arr[high] (which might be out of bounds if high drops to -1)?
Derivation Steps:
- We know the number of missing elements up to
highis:
- How many more missing numbers do we need beyond what
highhas already accounted for?
- Our final answer is the value at
highplus these extra numbers needed:
Since low = high + 1, this simplifies beautifully to:
This explains why returning low + K works universally, even when high falls off to -1 (e.g., when the array starts with numbers larger than ).
7. Complete Optimized Code Implementation
Here is the clean, production-ready implementation of the algorithm:
#include <vector>
using namespace std;
class Solution {
public:
int findKthPositive(vector<int>& arr, int k) {
int low = 0, high = arr.size() - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
// Calculate missing numbers up to index 'mid'
int missing = arr[mid] - (mid + 1);
if (missing < k) {
low = mid + 1; // Look to the right half
} else {
high = mid - 1; // Look to the left half
}
}
// Alternatively, return high + 1 + k;
return low + k;
}
};
8. Summary and Key Takeaways
- Problem Nature: Finding the -th missing positive number in a sorted increasing array requires tracking the count of missing integers relative to ideal indices.
- Sub-optimal vs. Optimal: Brute force scans elements in time, whereas Binary Search cuts the search space down to logarithmic time .
- Missing Count Formula: At any index
mid, the number of missing integers is given by . - Binary Search Invariant: We use binary search to find the exact boundary where the missing count transitions from less than to greater than or equal to .
- Pointer Cross-Over: When the loop terminates,
highandlowcross over, establishing a precise interpolation bracket. - The Elegant Result Formula: By algebraic reduction, the final answer simplifies cleanly to (or ), avoiding edge-case index out-of-bounds errors.
- Space Efficiency: The solution operates with auxiliary space complexity.