Find First and Last Position of Element in Sorted Array
Given an array of integers nums sorted in non-decreasing order, find the starting and ending position of a given target value.
If target is not found in the array, 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]
Example 2:
Input: nums = [5,7,7,8,8,10], target = 6
Output: [-1,-1]
Example 3:
Input: nums = [], target = 0
Output: [-1,-1]
Constraints:
- 0 <= nums.length <= 105
- -109 <= nums[i] <= 109
- nums is a non-decreasing array.
- -109 <= target <= 109
Step 1 : – Template Implementation
There are two main Binary Search templates: 1). Inclusive boundary search template (while (low <= high)) 2). Boundary convergence search template (while (low < high))
Based on our analysis of the problem statement, this is a target-based search problem where we need to find specific indices for a given target element. Therefore, we will use the standard inclusive boundary search template (while (low <= high)) as our foundation.
class Solution {
public int binarySearch(int[] nums, int target) {
int low = 0, high = nums.length - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (param1 == param2) {
return mid;
} else if (param1 < param2) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return -1;
}
}
Step 2 : – I/O parameters.
Template input parameters :- To calculate the value of param1, we access the element at index mid, which is nums[mid]. We replace param2 with the target value. Output parameter :- The standard template returns the index mid if the target is found, or -1 if it is not found.
class Solution {
public int binarySearch(int[] nums, int target) {
int low = 0, high = nums.length - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] < target) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return -1;
}
}
Step 3 : – Test case scenarios & Directional Search.
In standard Binary Search, returning mid immediately works when finding any single occurrence. However, for an array with duplicates like nums = [5, 7, 7, 8, 8, 10] and target = 8, returning on the first match could give either index 3 or index 4.
To find both the starting and ending boundaries, we introduce our main method searchRange to call our search logic twice:
-
Scenario 1: Finding the First Position (
isFirst == true)-
Test Case:
nums = [5, 7, 7, 8, 8, 10],target = 8 -
When
midpoints to index4(nums[4] == 8), we savebound = 4. -
Since
isFirstistrue, a target value might exist earlier on the left side. -
We shrink the right boundary:
high = mid - 1.
-
-
Scenario 2: Finding the Last Position (
isFirst == false)-
Test Case:
nums = [5, 7, 7, 8, 8, 10],target = 8 -
When
midpoints to index3(nums[3] == 8), we savebound = 3. -
Since
isFirstisfalse, a target value might exist later on the right side. -
We shrink the left boundary:
low = mid + 1.
-
class Solution {
public int[] searchRange(int[] nums, int target) {
int first = findBound(nums, target, true);
int last = findBound(nums, target, false);
return new int[] { first, last };
}
private int findBound(int[] nums, int target, boolean isFirst) {
int low = 0, high = nums.length - 1;
int bound = -1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (nums[mid] == target) {
bound = mid;
if (isFirst) {
high = mid - 1; // Scenario 1: Search left half for first occurrence
} else {
low = mid + 1; // Scenario 2: Search right half for last occurrence
}
} else if (nums[mid] < target) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return bound;
}
}
Step 4: – Handle corner test cases.
We evaluate how the code handles edge cases automatically:
-
Corner Case 1: Empty Array (
nums = [],target = 0)-
highis initialized to-1. The loop conditionlow <= high(0 <= -1) is false from the start. -
findBoundreturns the defaultbound = -1for bothfirstandlast, resulting in[-1, -1].
-
-
Corner Case 2: Target Not Present (
nums = [5, 7, 7, 8, 8, 10],target = 6)-
The condition
nums[mid] == targetis never met during execution. -
boundremains-1, and the method returns[-1, -1].
-
class Solution {
public int[] searchRange(int[] nums, int target) {
int first = findBound(nums, target, true);
int last = findBound(nums, target, false);
return new int[] { first, last };
}
private int findBound(int[] nums, int target, boolean isFirst) {
int low = 0, high = nums.length - 1;
int bound = -1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (nums[mid] == target) {
bound = mid;
if (isFirst) {
high = mid - 1;
} else {
low = mid + 1;
}
} else if (nums[mid] < target) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return bound;
}
}
Final Code : –
class Solution {
public int[] searchRange(int[] nums, int target) {
int first = findBound(nums, target, true);
int last = findBound(nums, target, false);
return new int[] { first, last };
}
private int findBound(int[] nums, int target, boolean isFirst) {
int low = 0, high = nums.length - 1;
int bound = -1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (nums[mid] == target) {
bound = mid;
if (isFirst) {
high = mid - 1;
} else {
low = mid + 1;
}
} else if (nums[mid] < target) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return bound;
}
}
