class Solution:
def searchRange(self, nums, target):
def findLeft(nums, target):
left, right = 0, len(nums) - 1
while left <= right:
mid = (left + right) // 2
if nums[mid] < target:
left = mid + 1
else:
right = mid - 1
return left
def findRight(nums, target):
left, right = 0, len(nums) - 1
while left <= right:
mid = (left + right) // 2
if nums[mid] <= target:
left = mid + 1
else:
right = mid - 1
return right
left_idx = findLeft(nums, target)
right_idx = findRight(nums, target)
if left_idx <= right_idx and left_idx < len(nums) and nums[left_idx] == target and nums[right_idx] == target:
return [left_idx, right_idx]
else:
return [-1, -1]
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int left = findLeft(nums, target);
int right = findRight(nums, target);
if (left <= right && left < nums.size() && right >= 0 && nums[left] == target && nums[right] == target)
return {left, right};
return {-1, -1};
}
int findLeft(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target)
left = mid + 1;
else
right = mid - 1;
}
return left;
}
int findRight(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] <= target)
left = mid + 1;
else
right = mid - 1;
}
return right;
}
};
class Solution {
public int[] searchRange(int[] nums, int target) {
int left = findLeft(nums, target);
int right = findRight(nums, target);
if (left <= right && left < nums.length && right >= 0 && nums[left] == target && nums[right] == target)
return new int[]{left, right};
return new int[]{-1, -1};
}
private int findLeft(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target)
left = mid + 1;
else
right = mid - 1;
}
return left;
}
private int findRight(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] <= target)
left = mid + 1;
else
right = mid - 1;
}
return right;
}
}
var searchRange = function(nums, target) {
function findLeft(nums, target) {
let left = 0, right = nums.length - 1;
while (left <= right) {
let mid = Math.floor((left + right) / 2);
if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return left;
}
function findRight(nums, target) {
let left = 0, right = nums.length - 1;
while (left <= right) {
let mid = Math.floor((left + right) / 2);
if (nums[mid] <= target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return right;
}
let leftIdx = findLeft(nums, target);
let rightIdx = findRight(nums, target);
if (leftIdx <= rightIdx && leftIdx < nums.length && nums[leftIdx] === target && nums[rightIdx] === target) {
return [leftIdx, rightIdx];
} else {
return [-1, -1];
}
};
Given a sorted array of integers nums
and a target value target
, you are asked to find the starting and ending positions of target
in nums
. If target
is not present in the array, you should return [-1, -1]
.
The problem emphasizes efficiency: your solution must have a runtime complexity of O(log n), taking advantage of the fact that nums
is sorted in non-decreasing order. The output should be a pair of indices: the first and last occurrence of target
.
target
is not found, return [-1, -1]
.nums
are integers, and nums
may be empty.
At first glance, you might consider scanning through nums
from start to finish, marking the first and last time you see target
. This brute-force approach works but is inefficient for large arrays because it checks every element, resulting in O(n) time complexity.
However, since nums
is sorted, we can do better. Binary search is ideal for sorted arrays, allowing us to quickly "zoom in" on the area where target
appears. The challenge is not just to find if target
exists, but to find its first and last positions. This requires a modified binary search: one to find the leftmost (first) index, and one to find the rightmost (last) index.
By splitting the problem into two searches (left and right bounds), we can maintain O(log n) efficiency and avoid unnecessary scanning.
The optimized solution leverages binary search to efficiently find the first and last positions of target
in nums
.
target
.nums[mid]
is less than target
, move the left boundary up.nums[mid]
is greater than or equal to target
, move the right boundary down.left
points to the first occurrence of target
(or where it would be).nums[mid]
is less than or equal to target
, move the left boundary up.nums[mid]
is greater than target
, move the right boundary down.right
points to the last occurrence of target
(or where it would be).target
.[-1, -1]
.This approach ensures we only use O(log n) time by never scanning more than necessary.
Let's walk through an example with nums = [5,7,7,8,8,10]
and target = 8
.
If target = 6
, both searches would find no valid index, so the result would be [-1, -1]
.
The optimized approach is significantly faster for large arrays, making full use of the array's sorted property.
This problem demonstrates the power of binary search for sorted arrays. By carefully adjusting the search conditions, we can efficiently find both the first and last occurrences of a target value in O(log n) time. The key insight is to treat the two searches (left and right bounds) separately, ensuring precision and efficiency. This approach is elegant, concise, and leverages fundamental algorithmic strategies every programmer should know.