Previous post: Binary search done right: longest increasing subsequence in O(n log n)
<aside> 💡 Problem statement: you are given a sorted array of positive integers, you have to identify k-th non-present positive integer element.
</aside>
Example:
array = [1, 2, 3, 5, 7, 8, 15]
^ missing 4
^ missing 6
^ missing 9, 10, 11, 12, 13, 14
Note that by taking k-th element and subtracting its index, we get the number of skipped elements up to this point (plus one):
array[0] - 0 = 1 # 0 elements skipped + 1
array[1] - 1 = 1 # the same
array[3] - 3 = 5 - 3 = 2 # element `4` is skipped + 1
array[6] - 6 = 15 - 6 = 9 # 8 elements skipped + 1
Therefore, the sequence $(\mathtt{array}[i] - i + 1)_{i=0}^{n-1}$ is non-decreasing. By using binary search, we can identify the left and right boundaries for the property “the number of skipped elements is less than or equal to k”.
def find_kth_nonpresent(array: list[int], k: int) -> int:
left = -1
right = len(array)
while right - left > 1:
mid = (left + right) // 2
if array[mid] - mid - 1 < k:
left = mid
else:
right = mid
# discussion continues
In the above code, we make sure that
left boundary satisfies the search condition. If we mathematically suppose that minus first element is $0$, then we have no skipped elements, which is less than k.right boundary does not satisfy the search condition. Indeed, appending a positive infinity to the array, we have an infinite amount of skipped elements.At the end of this piece, we have the left and right positions so that at left, there are less than k numbers skipped, and at right there are at least k numbers skipped.