Find First and Last Position of Element in Sorted Array medium
Problem Statement
Given a sorted array of integers nums
and an integer target
, return the indices of the first and last occurrences of target
in nums
. If target
is not found in nums
, return [-1, -1]
. You must write an algorithm with O(log n)
runtime complexity.
Example 1
Input: nums
= [5,7,7,8,8,10], target
= 8
Output: [3,4]
Explanation: The first occurrence of 8 is at index 3 and the last occurrence is at index 4.
Example 2
Input: nums
= [5,7,7,8,8,10], target
= 6
Output: [-1,-1]
Explanation: Because 6 does not exist in the array, we return [-1,-1].
Steps
The core idea is to use binary search twice: once to find the leftmost occurrence and once to find the rightmost occurrence of the target.
- Find Leftmost Occurrence:
- Perform a binary search. If the target is found, continue searching in the left half to see if there's an earlier occurrence. The search stops when we find the target and the element to its left is different or we've reached the beginning of the array.
- Find Rightmost Occurrence:
- Perform a binary search. If the target is found, continue searching in the right half to see if there's a later occurrence. The search stops when we find the target and the element to its right is different or we've reached the end of the array.
- Return Results: If either search doesn't find the target, return
[-1, -1]
. Otherwise, return the indices of the leftmost and rightmost occurrences.
Explanation
The binary search approach ensures a time complexity of O(log n) because we are repeatedly halving the search space. The crucial part is modifying the standard binary search to find the extreme occurrences (leftmost and rightmost) by selectively searching in the left or right subarray depending on whether we've found a match.
Code
function searchRange(nums: number[], target: number): number[] {
const leftmost = findLeftmost(nums, target);
const rightmost = findRightmost(nums, target);
return [leftmost, rightmost];
};
function findLeftmost(nums: number[], target: number): number {
let left = 0;
let right = nums.length - 1;
let leftmostIndex = -1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (nums[mid] === target) {
leftmostIndex = mid;
right = mid - 1; // Continue searching in the left half
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return leftmostIndex;
}
function findRightmost(nums: number[], target: number): number {
let left = 0;
let right = nums.length - 1;
let rightmostIndex = -1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (nums[mid] === target) {
rightmostIndex = mid;
left = mid + 1; // Continue searching in the right half
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return rightmostIndex;
}
Complexity
- Time Complexity: O(log n) - Due to the use of binary search.
- Space Complexity: O(1) - Constant extra space is used. The algorithm operates in-place.