Find First and Last Position of Element in Sorted Array
Return the [first, last] indices of target via two boundary binary searches (lower_bound and upper_bound).
3 min read
binary-search
Problem#
Sorted array. Return [first, last] indices of target, or [-1, -1] if absent.
Examples#
nums = [5,7,7,8,8,10], target = 8→[3, 4]nums = [5,7,7,8,8,10], target = 6→[-1, -1]
Constraints#
0 <= n <= 10^5.
Approach#
Two binary searches: lower_bound(target) and upper_bound(target) - 1. If lo == n or nums[lo] != target, return [-1, -1].
Solution#
class Solution {public: vector<int> searchRange(vector<int>& nums, int target) { auto lo = lower_bound(nums.begin(), nums.end(), target); if (lo == nums.end() || *lo != target) return {-1, -1}; auto hi = upper_bound(nums.begin(), nums.end(), target); return {(int)(lo - nums.begin()), (int)(hi - nums.begin()) - 1}; }};import "sort"
func searchRange(nums []int, target int) []int { lo := sort.SearchInts(nums, target) if lo == len(nums) || nums[lo] != target { return []int{-1, -1} } hi := sort.SearchInts(nums, target+1) return []int{lo, hi - 1}}import bisectfrom typing import List
class Solution: def searchRange(self, nums: List[int], target: int) -> List[int]: lo = bisect.bisect_left(nums, target) if lo == len(nums) or nums[lo] != target: return [-1, -1] hi = bisect.bisect_right(nums, target) return [lo, hi - 1]function searchRange(nums, target) { const n = nums.length; const lowerBound = (t) => { let lo = 0, hi = n; while (lo < hi) { const m = (lo + hi) >> 1; if (nums[m] < t) lo = m + 1; else hi = m; } return lo; }; const lo = lowerBound(target); if (lo === n || nums[lo] !== target) return [-1, -1]; const hi = lowerBound(target + 1); return [lo, hi - 1];}class Solution { public int[] searchRange(int[] nums, int target) { int lo = lowerBound(nums, target); if (lo == nums.length || nums[lo] != target) return new int[]{-1, -1}; int hi = lowerBound(nums, target + 1); return new int[]{lo, hi - 1}; }
private int lowerBound(int[] nums, int t) { int lo = 0, hi = nums.length; while (lo < hi) { int m = (lo + hi) >>> 1; if (nums[m] < t) lo = m + 1; else hi = m; } return lo; }}function searchRange(nums: number[], target: number): number[] { const n = nums.length; const lowerBound = (t: number): number => { let lo = 0, hi = n; while (lo < hi) { const m = (lo + hi) >> 1; if (nums[m] < t) lo = m + 1; else hi = m; } return lo; }; const lo = lowerBound(target); if (lo === n || nums[lo] !== target) return [-1, -1]; const hi = lowerBound(target + 1); return [lo, hi - 1];}Editorial#
lower_bound finds the first index ≥ target; upper_bound finds the first index > target. Their difference is the count of targets; their endpoints minus 1 give first and last index. Standard library handles both in O(log n).
Complexity#
- Time: O(log n).
- Space: O(1).
Concept revision#
Related#