Topics › Binary Search › BS on Answers
Kth Missing Positive Number
LeetCode ↗ Java source ↗ Edit this article ↗
Jump to code:
Problem
You are given a strictly increasing array arr of positive integers and an integer k. Consider the positive integers 1, 2, 3, ... that do not appear in arr, in increasing order. Return the k-th of them.
Constraints: 1 ≤ n ≤ 1000, 1 ≤ arr[i] ≤ 1000, 1 ≤ k ≤ 1000, arr[i] < arr[j] for i < j.
Example 1
arr = [2, 3, 4, 7, 11], k = 5
answer = 9 // missing numbers are 1, 5, 6, 8, 9, 10, 12, ...; the 5th is 9
Example 2
arr = [1, 2, 3, 4], k = 2
answer = 6 // nothing is missing inside the array, so the missing numbers start at 5: 5, 6, 7, ...
Approach 1: Brute force, count the missing numbers one by one
Intuition. Do exactly what the statement says. Walk through the positive integers starting at 1, and for each one ask “is it in the array?”. A hash set answers that in constant time. Count the ones that are absent and stop at the k-th.
Algorithm.
- Put every element of
arrinto a set. - Set
missing = 0,num = 0. - While
missing < k: incrementnum; ifnumis not in the set, incrementmissing. - Return
num.
static int bruteForce(int[] arr, int k) {
Set<Integer> present = new HashSet<>();
for (int x : arr) present.add(x);
int missing = 0, num = 0;
while (missing < k) {
num++;
if (!present.contains(num)) missing++;
}
return num;
}
Complexity. Time O(n + k): the loop stops once k missing numbers have been found, and it skips at most n present numbers along the way, so it runs at most n + k times. Space O(n) for the set.
For the given limits this is instant, but it ignores the fact that the array is sorted, and it scales with k rather than with log n.
Approach 2: Better, one pass that shifts the answer
Intuition. Suppose the array were empty. Then the k-th missing positive is simply k. Now add the array elements back one at a time from the left. An element x ≤ k occupies a slot among the first k positive integers, which pushes the k-th missing number one step to the right: the new answer is k + 1. Repeating this for every element that is not larger than the current answer gives the final answer. Because the array is strictly increasing, once an element exceeds the running answer, all later elements do too and cannot affect it, so we can stop.
Algorithm.
- For each
xinarrfrom left to right:- If
x ≤ k, incrementk. - Otherwise stop.
- If
- Return
k.
static int better(int[] arr, int k) {
for (int x : arr) {
if (x <= k) k++;
else break;
}
return k;
}
Complexity. Time O(n): one pass, often shorter because of the early break. Space O(1).
Trace on arr = [2, 3, 4, 7, 11], k = 5: 2 ≤ 5 gives k = 6; 3 ≤ 6 gives 7; 4 ≤ 7 gives 8; 7 ≤ 8 gives 9; 11 > 9, stop. Answer 9.
Why is “x ≤ k then k++” correct rather than, say, “x ≤ original k”? Because each time we bump k, the range [1, k] grows by one, and the next array element must be compared with the new range. The invariant after processing the first i elements is: k equals the k-th missing positive if the array consisted only of those i elements.
Approach 3: Optimal, binary search on the index
Intuition. Use the sortedness properly. For index i, how many positive integers smaller than arr[i] are missing? If nothing were missing, the element at index i would be i + 1 (positions are 1, 2, 3, ...). The actual element is arr[i], so exactly
missing(i) = arr[i] - (i + 1)
positives are missing before arr[i]. Because arr is strictly increasing, missing(i) is non-decreasing in i: each step either keeps it (consecutive values) or raises it (a gap). We are looking for the first index whose missing count is at least k; the k-th missing number lies just before that element. Standard binary search for the first index with missing(i) ≥ k: afterwards hi is the last index with missing(hi) < k and lo = hi + 1.
With hi known, the answer is arr[hi] + (k - missing(hi)): start from arr[hi], which has missing(hi) missing numbers before it, and count k - missing(hi) more positions forward (all of which are missing, because the next array element arr[hi+1] has missing ≥ k and therefore comes after the k-th gap). Substituting missing(hi) = arr[hi] - hi - 1 simplifies that to hi + 1 + k, which is lo + k. The arr[hi] terms cancel, so the formula also works when hi = -1 (the answer is before the first element) and when hi = n - 1 (the answer is past the end).
Algorithm.
- Set
lo = 0,hi = n - 1. - While
lo ≤ hi:mid = lo + (hi - lo) / 2,missing = arr[mid] - (mid + 1).- If
missing < k, setlo = mid + 1; else sethi = mid - 1.
- Return
lo + k.
static int optimal(int[] arr, int k) {
int lo = 0, hi = arr.length - 1;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
int missing = arr[mid] - (mid + 1); // positives missing before arr[mid]
if (missing < k) lo = mid + 1;
else hi = mid - 1;
}
return lo + k; // equivalently hi + 1 + k
}
Dry run on arr = [2, 3, 4, 7, 11], k = 5. The missing values per index are [1, 1, 1, 3, 6].
| lo | hi | mid | arr[mid] | missing(mid) | < 5? | next |
|---|---|---|---|---|---|---|
| 0 | 4 | 2 | 4 | 1 | yes | lo = 3 |
| 3 | 4 | 3 | 7 | 3 | yes | lo = 4 |
| 4 | 4 | 4 | 11 | 6 | no | hi = 3, stop |
lo = 4, answer 4 + 5 = 9. Check with the formula: arr[3] + (5 - 3) = 7 + 2 = 9.
Complexity. Time O(log n). Space O(1).
Edge cases and pitfalls
- Answer before the first element.
[5, 6, 7],k = 2:missing(0) = 4 ≥ 2, sohibecomes-1,lo = 0, answer0 + 2 = 2. Correct, since1and2are the first two missing numbers. - Answer past the end.
[1, 2, 3, 4],k = 1000: everymissingis0 < 1000, soloends at4and the answer is1004. - Single element.
[1],k = 1gives2;[2],k = 1gives1. - Off-by-one in
missing. The expected value at indexiisi + 1, noti. Writingarr[mid] - midshifts every count by one and produces wrong answers for almost every input. - Returning
hi + korlo + k + 1. The correct expression islo + k(equivalentlyhi + 1 + k). Re-derive it fromarr[hi] + (k - missing(hi))if you forget. - Duplicates. The array is strictly increasing, so
missing(i)is well defined and non-decreasing. With duplicates the formula breaks; de-duplicate first if a variant allows them. - Approach 2 needs sortedness too. The early
breakand the shifting argument both rely on the increasing order. The hash-set brute force is the only method that works on unsorted input.
Related problems
- Single element in a Sorted Array and Find peak element are the other binary searches in this step where the decision at
midcomes from a relationship between value and index rather than from a plain comparison with a target. - Find square root of a number introduces the “binary search on the answer” template that the surrounding problems use.