This problem involves finding a target value within a sorted array of unknown size. We can only access the array elements using an ArrayReader
interface, which returns the value at a given index or 2^31 - 1
if the index is out of bounds. The challenge is to solve this efficiently, aiming for O(log n) time complexity, where n is the index of the target element (if it exists).
The solution leverages a modified binary search strategy. Because the array size is unknown, we first need to determine an upper bound for our search space. We do this by repeatedly doubling an index (r
) until we find an element greater than or equal to the target. This initial phase ensures we have a range where the target might exist.
Once we have this upper bound, we can perform a standard binary search within that range. This approach is efficient because the initial doubling phase ensures a relatively small search space for the binary search.
Find Upper Bound:
r = 1
.ArrayReader.get(r) < target
, double r
(r <<= 1
or r *= 2
). This finds a value r
such that ArrayReader.get(r)
is greater than or equal to the target, or we reach the end of the array.Binary Search:
l = r >> 1
(or l = r / 2
). This is our lower bound.[l, r]
.
mid = (l + r) >> 1
(or mid = (l + r) / 2
).ArrayReader.get(mid) >= target
, update r = mid
. The target is in the lower half (or potentially at mid
).l = mid + 1
. The target is in the upper half (if it exists).l
and r
converge.Check and Return:
ArrayReader.get(l) == target
. If true, return l
(the index of the target). If false, return -1
(target not found).Finding Upper Bound: The loop doubling r
runs at most log₂(n) times, where n is the index of the target (or the last element if the target is not present). This is because each iteration doubles the range we're searching.
Binary Search: The binary search on the interval [l, r]
takes O(log(n)) time in the worst case.
Therefore, the overall time complexity is O(log n), dominated by the binary search phase.
The space complexity is O(1) because we are only using a few constant extra variables to store the indices l
, r
, and mid
.
The provided code in various languages accurately implements the algorithm described above. Each version follows the same structure: the initial doubling phase followed by the binary search and final check. The bitwise operators (<<=
, >>
, etc.) are used for efficiency in some languages to perform multiplication and division by 2.