Binary Search

easy

You are given nums, an array of integers sorted in ascending order, and an integer target.

Return the index of target if it exists in nums. If it does not, return -1.

The catch: your algorithm must run in O(log n) time. A plain left-to-right scan does not qualify — the array is sorted for a reason, and the whole game is to exploit that.

Hints

The array is sorted. What does comparing target against the middle element tell you about the rest of the array?
If nums[mid] < target, then every element at or before mid is also too small — the target cannot be in the left half. One comparison just eliminated half the candidates.
Keep two pointers lo and hi bounding the region that could still contain target. Loop while lo <= hi, probe mid = lo + (hi - lo) / 2, and shrink to the surviving half. Halving each step is exactly O(log n).

Common doubts

The statement requires O(log n) as part of the problem, not just a fast wall-clock time. At n = 10^4 a scan finishes quickly, but it does not demonstrate the required algorithm — and in an interview, scanning a sorted array signals you missed the point of the question.
In fixed-width integer languages like C++ and Go, lo + hi can overflow when both are near the type's maximum. lo + (hi - lo) / 2 computes the same midpoint without ever forming the large sum. Python integers cannot overflow, but the habit is worth keeping everywhere.
No — and it fails silently. The discard step relies on order: nums[mid] < target must imply everything left of mid is also smaller. Without sorting, you may throw away the half that actually contains the target and get -1 for a present element.
With lo <= hi, a one-element window (lo == hi) still gets probed — necessary, since that lone element might be the target. With lo < hi you would exit before checking it. The pairing rule: lo <= hi goes with mid + 1 / mid - 1 updates.

Interview follow-ups

That is the lower-bound variant: instead of returning -1, return lo after the loop — lo always lands on the first position with a value >= target. Same loop, different return.
On a match, do not return immediately — record mid as a candidate and keep searching left (hi = mid - 1). The last recorded candidate is the leftmost occurrence, still in O(log n).
One half of any [lo, hi] window is still properly sorted. Check which half is sorted, decide whether the target lies in it, and discard the other — binary search survives with one extra comparison per step.
Yes — any monotonic yes/no question over a numeric range: the smallest capacity that ships packages in time, the minimum speed to finish eating bananas. This pattern is called binary search on the answer, and it is where this template really pays off.

Fun facts

  • Binary search was first published in 1946, yet a study famously found that the first version proven fully correct did not appear until 1962 — sixteen years of off-by-one bugs in a six-line algorithm.
  • The (lo + hi) / 2 overflow bug hid inside widely used standard-library implementations for roughly two decades before being noticed and fixed.
  • The halving idea powers far more than arrays: git bisect binary-searches your commit history to find the commit that broke the build.
  • For n = 10^4 elements, binary search needs at most 14 probes; even for four billion elements it needs just 32.

Asked at

GoogleAmazonMicrosoftMetaAppleBloombergAdobe
Frequently Sometimes Occasionally
Example 1
Input: nums = [-1,0,3,5,9,12], target = 9
Output: 4
9 exists in nums and its index is 4.
Example 2
Input: nums = [-1,0,3,5,9,12], target = 2
Output: -1
2 does not exist in nums, so we return -1.
Constraints

- 1 <= nums.length <= 10^4 - -10^4 < nums[i], target < 10^4 - All the integers in nums are unique. - nums is sorted in ascending order.

Solve this problem →