🧠
Modified Binary Search to Find Leftmost and Rightmost Indices
💡 This approach improves efficiency by using binary search twice to find the boundaries, leveraging the sorted property of the array.
Intuition
Use binary search to find the leftmost occurrence of the target, then use binary search again to find the rightmost occurrence.
Algorithm
- Define a helper function to find the left boundary of the target using binary search.
- Define a helper function to find the right boundary of the target using binary search.
- Call the left boundary function to find the first occurrence.
- Call the right boundary function to find the last occurrence.
- Return the pair of indices.
💡 The key is modifying binary search to continue searching even after finding the target to find the extreme boundaries.
def searchRange(nums, target):
def findLeft():
left, right = 0, len(nums) - 1
idx = -1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
idx = mid
right = mid - 1 # continue searching left
elif nums[mid] < target:
left = mid + 1
else:
right = mid - 1
return idx
def findRight():
left, right = 0, len(nums) - 1
idx = -1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
idx = mid
left = mid + 1 # continue searching right
elif nums[mid] < target:
left = mid + 1
else:
right = mid - 1
return idx
return [findLeft(), findRight()]
# Driver code
if __name__ == '__main__':
print(searchRange([5,7,7,8,8,10], 8)) # Output: [3,4]
Line Notes
def findLeft():Helper to find leftmost index of target
while left <= right:Standard binary search loop condition
if nums[mid] == target:Update idx and move right pointer to find left boundary
elif nums[mid] < target:Move left pointer right to search higher values
import java.util.*;
public class Solution {
public static int[] searchRange(int[] nums, int target) {
int leftIdx = findLeft(nums, target);
int rightIdx = findRight(nums, target);
return new int[]{leftIdx, rightIdx};
}
private static int findLeft(int[] nums, int target) {
int left = 0, right = nums.length - 1, idx = -1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
idx = mid;
right = mid - 1; // continue searching left
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return idx;
}
private static int findRight(int[] nums, int target) {
int left = 0, right = nums.length - 1, idx = -1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
idx = mid;
left = mid + 1; // continue searching right
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return idx;
}
public static void main(String[] args) {
System.out.println(Arrays.toString(searchRange(new int[]{5,7,7,8,8,10}, 8)));
}
}
Line Notes
int leftIdx = findLeft(nums, target);Find left boundary index
while (left <= right)Binary search loop condition
if (nums[mid] == target) {Update idx and move right pointer to find left boundary
else if (nums[mid] < target)Move left pointer right to search higher values
#include <iostream>
#include <vector>
using namespace std;
int findLeft(const vector<int>& nums, int target) {
int left = 0, right = (int)nums.size() - 1, idx = -1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
idx = mid;
right = mid - 1; // continue searching left
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return idx;
}
int findRight(const vector<int>& nums, int target) {
int left = 0, right = (int)nums.size() - 1, idx = -1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
idx = mid;
left = mid + 1; // continue searching right
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return idx;
}
vector<int> searchRange(vector<int>& nums, int target) {
return {findLeft(nums, target), findRight(nums, target)};
}
int main() {
vector<int> nums = {5,7,7,8,8,10};
vector<int> res = searchRange(nums, 8);
cout << "[" << res[0] << "," << res[1] << "]" << endl;
return 0;
}
Line Notes
int idx = -1;Initialize index to -1 meaning target not found
while (left <= right)Binary search loop condition
if (nums[mid] == target)Update idx and move right pointer to find left boundary
else if (nums[mid] < target)Move left pointer right to search higher values
function searchRange(nums, target) {
function findLeft() {
let left = 0, right = nums.length - 1, idx = -1;
while (left <= right) {
let mid = Math.floor((left + right) / 2);
if (nums[mid] === target) {
idx = mid;
right = mid - 1; // continue searching left
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return idx;
}
function findRight() {
let left = 0, right = nums.length - 1, idx = -1;
while (left <= right) {
let mid = Math.floor((left + right) / 2);
if (nums[mid] === target) {
idx = mid;
left = mid + 1; // continue searching right
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return idx;
}
return [findLeft(), findRight()];
}
// Test
console.log(searchRange([5,7,7,8,8,10], 8)); // [3,4]
Line Notes
let idx = -1;Initialize index to -1 meaning target not found
while (left <= right)Binary search loop condition
if (nums[mid] === target)Update idx and move right pointer to find left boundary
else if (nums[mid] < target)Move left pointer right to search higher values
Each binary search runs in logarithmic time, so total time is O(log n). Space is constant as no extra data structures are used.
💡 For n=100,000 elements, this means about 2 * 17 = 34 operations, which is very efficient.
Interview Verdict: Accepted and optimal
This approach meets the problem's time complexity requirement and is the expected solution in interviews.