Find First and Last Position of Element in Sorted Array

medium

You're given an array of integers nums, sorted in non-decreasing order, and an integer target.

Return the starting and ending positions of target in nums as an array [first, last] — the index of its first occurrence and the index of its last occurrence.

If target does not appear in nums, return [-1, -1].

Your algorithm must run in O(log n) time.

Hints

The array is sorted and the required time is O(log n) — what family of algorithms does that combination always point to?
A standard binary search stops at any match. What happens if, on a match, you refuse to stop and keep searching left anyway?
Write one helper lowerBound(x) returning the first index with nums[i] >= x. Then first = lowerBound(target) and last = lowerBound(target + 1) - 1.

Common doubts

lowerBound(target + 1) is the first index whose value is strictly greater than target. Since all copies of target sit in one contiguous block (the array is sorted), that index is exactly one slot past the block — step back one and you're on the last copy.
Because the expansion is linear. On an array that is all targets (e.g. every element is 8), you'd expand across the whole array — O(n), exactly what the statement forbids. The boundary search stays O(log n) no matter how wide the block is.
n — one slot past the end. That's why the absence check first == n must run before reading nums[first]; it also gracefully covers the empty-array case.
Not here: target is at most 10^9, and 10^9 + 1 fits comfortably in every language's integer type used by the solutions. In stricter settings, an explicit upperBound helper avoids the addition entirely.

Interview follow-ups

It falls straight out of this solution: count = last - first + 1, or equivalently lowerBound(target + 1) - lowerBound(target) — no extra passes needed.
Yes — change the comparison to nums[mid] <= x and the helper returns the first index with a value strictly greater than x. Then last = upperBound(target) - 1. Same template, one flipped comparison.
First locate the rotation point with a modified binary search (compare mid against the ends), then run the boundary searches inside the correct half — still O(log n) overall. That's the Search in Rotated Sorted Array pattern.

Fun facts

  • C++ ships this exact idea in its standard library: std::lower_bound and std::upper_bound, and the pair of edges together is literally called std::equal_range.
  • The boundary template — first index where a condition flips from false to true — reappears everywhere: Search Insert Position, First Bad Version, Koko Eating Bananas, and every binary-search-on-the-answer problem.

Asked at

GoogleAmazonMetaMicrosoftBloomberg
Frequently Sometimes Occasionally
Example 1
Input: nums = [5,7,7,8,8,10], target = 8
Output: [3,4]
The value 8 first appears at index 3 and last appears at index 4.
Example 2
Input: nums = [5,7,7,8,8,10], target = 6
Output: [-1,-1]
6 is not in the array, so both positions are -1.
Example 3
Input: nums = [], target = 0
Output: [-1,-1]
An empty array contains nothing — return [-1, -1].
Constraints

- 0 <= nums.length <= 10^5 - -10^9 <= nums[i] <= 10^9 - nums is sorted in non-decreasing order - -10^9 <= target <= 10^9

Solve this problem →