Given an array of integers nums sorted in non-decreasing order, find the starting and ending position of a given target value. If the target is not found in the array, return [-1, -1].
Input: nums = [5, 7, 7, 8, 8, 10], target = 8
Output: [3, 4]
Explanation:The target is 8, and it appears in the array at indices 3 and 4, so the output is [3,4]
Input: nums = [5, 7, 7, 8, 8, 10], target = 6
Output: [-1, -1]
Expalantion: The target is 6, which is not present in the array. Therefore, the output is [-1, -1].
Input: nums = [5, 7, 7, 8, 8, 10], target = 5
The naive approach to solve this problem would be a linear traversal of the given array. We traverse the array and store the index where the target appears the first time, in both the first and last occurrence variables. We update the last variable when we again encounter an index where value is equal to the target.
first
and last
, initialized to -1 to store the first and last occurrences of the target value.first
and last
.last
variable with the current index.Dry Run: Please refer to the video for the dry-run.
#include <bits/stdc++.h>
using namespace std;
class Solution
{
public:
vector<int> searchRange(vector<int>& nums, int target)
{
// Initialize variables to store first and last occurrences
int first = -1, last = -1;
vector<int> ans;
// Iterate through the array
for (int i = 0; i < nums.size(); i++) {
// Check if current element is the target
if (nums[i] == target) {
if (first == -1) first = i;
last = i;
}
}
// Store the results in the answer vector
ans.push_back(first);
ans.push_back(last);
return ans;
}
};
int main()
{
vector<int> nums = {5, 7, 7, 8, 8, 10};
int target = 8;
// Create an instance of the Solution class
Solution sol;
// Function call to find the first and last occurrences
vector<int> result = sol.searchRange(nums, target);
cout << "The first and last occurrences are at indices: "
<< result[0] << " and " << result[1] << "\n";
return 0;
}
import java.util.*;
class Solution {
public int[] searchRange(int[] nums, int target) {
// Initialize variables to store first and last occurrences
int first = -1, last = -1;
int[] ans = new int[2];
// Iterate through the array
for (int i = 0; i < nums.length; i++) {
// Check if current element is the target
if (nums[i] == target) {
if (first == -1) first = i;
last = i;
}
}
// Store the results in the answer array
ans[0] = first;
ans[1] = last;
return ans;
}
public static void main(String[] args) {
int[] nums = {5, 7, 7, 8, 8, 10};
int target = 8;
// Create an instance of the Solution class
Solution sol = new Solution();
// Function call to find the first and last occurrences
int[] result = sol.searchRange(nums, target);
System.out.println("The first and last occurrences are at indices: "
+ result[0] + " and " + result[1]);
}
}
class Solution:
def searchRange(self, nums, target):
# Initialize variables to store first and last occurrences
first, last = -1, -1
# Iterate through the array
for i in range(len(nums)):
# Check if current element is the target
if nums[i] == target:
if first == -1:
first = i
last = i
# Return the results as a list
return [first, last]
# Example usage
if __name__ == "__main__":
nums = [5, 7, 7, 8, 8, 10]
target = 8
# Create an instance of the Solution class
sol = Solution()
# Function call to find the first and last occurrences
result = sol.searchRange(nums, target)
print(f"The first and last occurrences are at indices: {result[0]} and {result[1]}")
class Solution {
searchRange(nums, target) {
// Initialize variables to store first and last occurrences
let first = -1, last = -1;
// Iterate through the array
for (let i = 0; i < nums.length; i++) {
// Check if current element is the target
if (nums[i] === target) {
if (first === -1)
first = i;
last = i;
}
}
// Return the results as an array
return [first, last];
}
}
// Example usage
const nums = [5, 7, 7, 8, 8, 10];
const target = 8;
// Create an instance of the Solution class
const sol = new Solution();
// Function call to find the first and last occurrences
const result = sol.searchRange(nums, target);
console.log(`The first and last occurrences are at indices: ${result[0]} and ${result[1]}`);
Time Complexity: O(N), where N is the size of the given array. This is because we are performing a linear search through the array to find the first and last occurrences of the target element.
Space Complexity: O(1), as we are not using any extra space that grows with the input size. We are only using a few additional variables to store indices and results.
In this approach, we use the lower bound and upper bound values to get the first and last occurrences of the target element. Lower bound of the target element will provide us index of the first occurrence of the target element. And, upper bound will provide the index of the element just after the last appearance of the target. So, we could return the lower bound and the upper bound - 1, as the first and last occurrences of the target in the given array.
Dry Run: Please refer to the video for the dry-run.
#include <bits/stdc++.h>
using namespace std;
class Solution
{
private:
// Function to find the lower bound of the target
int lowerBound(vector<int> &nums, int target) {
int low = 0, high = nums.size() - 1;
int ans = nums.size();
// Applying binary search algorithm
while(low <= high) {
int mid = (low + high) / 2;
/* If the middle element is greater than
or equal to the target element update
the answer as mid and eliminate the right half */
if(nums[mid] >= target) {
ans = mid;
high = mid - 1;
}
/* If the middle element is smaller than
the target element then we eliminate
the left half */
else {
low = mid + 1;
}
}
return ans;
}
// Function to find the upper bound of the target
int upperBound(vector<int> &nums, int target) {
int low = 0, high = nums.size() - 1;
int ans = nums.size();
// Applying binary search algorithm
while(low <= high) {
int mid = (low + high) / 2;
/* If the middle element is greater than
the target element update the answer
as mid and eliminate the right half */
if(nums[mid] > target) {
ans = mid;
high = mid - 1;
}
/* If the middle element is greater than
or equal to the target element
eliminate the right half */
else {
low = mid + 1;
}
}
return ans;
}
public:
// Function to find the first and last occurrences of the target
vector<int> searchRange(vector<int> &nums, int target) {
// Function call to find the first occurrence (lower bound)
int firstOcc = lowerBound(nums, target);
// Check if the target is present in the array or not
if(firstOcc == nums.size() || nums[firstOcc] != target) return {-1, -1};
// Function call to find the last occurrence (upper bound)
int lastOcc = upperBound(nums, target) - 1;
return {firstOcc, lastOcc};
}
};
int main()
{
vector<int> nums = {5, 7, 7, 8, 8, 10};
int target = 8;
// Create an instance of the Solution class
Solution sol;
// Function call to find the first and last occurrences
vector<int> result = sol.searchRange(nums, target);
cout << "The first and last occurrences are at indices: "
<< result[0] << " and " << result[1] << "\n";
return 0;
}
import java.util.*;
class Solution {
private int lowerBound(int[] nums, int target) {
int low = 0, high = nums.length - 1;
int ans = nums.length;
// Applying binary search algorithm
while(low <= high) {
int mid = (low + high) / 2;
/* If the middle element is greater than
or equal to the target element update
the answer as mid and eliminate the right half */
if(nums[mid] >= target) {
ans = mid;
high = mid - 1;
}
/* If the middle element is smaller than
the target element then we eliminate
the left half */
else {
low = mid + 1;
}
}
return ans;
}
private int upperBound(int[] nums, int target) {
int low = 0, high = nums.length - 1;
int ans = nums.length;
// Applying binary search algorithm
while(low <= high) {
int mid = (low + high) / 2;
/* If the middle element is greater than
the target element update the answer
as mid and eliminate the right half */
if(nums[mid] > target) {
ans = mid;
high = mid - 1;
}
/* If the middle element is greater than
or equal to the target element
eliminate the right half */
else {
low = mid + 1;
}
}
return ans;
}
public int[] searchRange(int[] nums, int target) {
// Function call to find the first occurrence (lower bound)
int firstOcc = lowerBound(nums, target);
// Check if the target is present in the array or not
if(firstOcc == nums.length || nums[firstOcc] != target) return new int[]{-1, -1};
// Function call to find the last occurrence (upper bound)
int lastOcc = upperBound(nums, target) - 1;
return new int[]{firstOcc, lastOcc};
}
public static void main(String[] args) {
int[] nums = {5, 7, 7, 8, 8, 10};
int target = 8;
// Create an instance of the Solution class
Solution sol = new Solution();
// Function call to find the first and last occurrences
int[] result = sol.searchRange(nums, target);
System.out.println("The first and last occurrences are at indices: "
+ result[0] + " and " + result[1]);
}
}
class Solution:
def lowerBound(self, nums, target):
low, high = 0, len(nums) - 1
ans = len(nums)
# Applying binary search algorithm
while low <= high:
mid = (low + high) // 2
# If the middle element is greater than
# or equal to the target element update
# the answer as mid and eliminate the right half
if nums[mid] >= target:
ans = mid
high = mid - 1
# If the middle element is smaller than
# the target element then we eliminate
# the left half
else:
low = mid + 1
return ans
def upperBound(self, nums, target):
low, high = 0, len(nums) - 1
ans = len(nums)
# Applying binary search algorithm
while low <= high:
mid = (low + high) // 2
# If the middle element is greater than
# the target element update the answer
# as mid and eliminate the right half
if nums[mid] > target:
ans = mid
high = mid - 1
# If the middle element is greater than
# or equal to the target element
# eliminate the right half
else:
low = mid + 1
return ans
def searchRange(self, nums, target):
# Function call to find the first occurrence (lower bound)
firstOcc = self.lowerBound(nums, target)
# Check if the target is present in the array or not
if firstOcc == len(nums) or nums[firstOcc] != target:
return [-1, -1]
# Function call to find the last occurrence (upper bound)
lastOcc = self.upperBound(nums, target) - 1
return [firstOcc, lastOcc]
if __name__ == "__main__":
nums = [5, 7, 7, 8, 8, 10]
target = 8
# Create an instance of the Solution class
sol = Solution()
# Function call to find the first and last occurrences
result = sol.searchRange(nums, target)
print("The first and last occurrences are at indices:", result[0], "and", result[1])
class Solution {
// Function to find the lower bound of the target
lowerBound(nums, target) {
let low = 0, high = nums.length - 1;
let ans = nums.length;
// Applying binary search algorithm
while(low <= high) {
let mid = Math.floor((low + high) / 2);
/* If the middle element is greater than
or equal to the target element update
the answer as mid and eliminate the right half */
if(nums[mid] >= target) {
ans = mid;
high = mid - 1;
}
/* If the middle element is smaller than
the target element then we eliminate
the left half */
else {
low = mid + 1;
}
}
return ans;
}
// Function to find the upper bound of the target
upperBound(nums, target) {
let low = 0, high = nums.length - 1;
let ans = nums.length;
// Applying binary search algorithm
while(low <= high) {
let mid = Math.floor((low + high) / 2);
/* If the middle element is greater than
the target element update the answer
as mid and eliminate the right half */
if(nums[mid] > target) {
ans = mid;
high = mid - 1;
}
/* If the middle element is greater than
or equal to the target element
eliminate the right half */
else {
low = mid + 1;
}
}
return ans;
}
// Function to find the first and last occurrences of the target
searchRange(nums, target) {
// Function call to find the first occurrence (lower bound)
let firstOcc = this.lowerBound(nums, target);
// Check if the target is present in the array or not
if(firstOcc == nums.length || nums[firstOcc] != target) return [-1, -1];
// Function call to find the last occurrence (upper bound)
let lastOcc = this.upperBound(nums, target) - 1;
return [firstOcc, lastOcc];
}
}
let nums = [5, 7, 7, 8, 8, 10];
let target = 8;
// Create an instance of the Solution class
let sol = new Solution();
// Function call to find the first and last occurrences
let result = sol.searchRange(nums, target);
console.log("The first and last occurrences are at indices:", result[0], "and", result[1]);
Time Complexity: 2*O(log N), where N is the size of the given array. Both the lowerBound
and upperBound
functions perform a binary search, which operates in logarithmic time. Thus, the overall time complexity is O(log N).
Space Complexity: O(1), as we are using a constant amount of extra space regardless of the input size. The space used by the variables low
, high
, mid
, and ans
does not depend on the size of the input array.
The optimal solution of this problem uses binary search to look for the first and last occurrences of the target element. While calculating the first occurrence, keep in mind that every time we get an element equal to target, look for the target again in its left half. Similarly, for getting the last occurrence, keep in mind that when we get an element equal to target, we look for the target again in its right half, so we get the last index where the target appears.
#include <bits/stdc++.h>
using namespace std;
class Solution
{
private:
// Function to find the first occurrence of the target
int firstOccurrence(vector<int> &nums, int target) {
int low = 0, high = nums.size() - 1;
int first = -1;
// Applying Binary Search Algorithm
while(low <= high) {
int mid = low + (high - low) / 2;
/* If the target element is found, we
update the first variable to mid and
eliminate the right half to look for
more smaller index where target is present */
if(nums[mid] == target) {
first = mid;
high = mid - 1;
}
/* If middle element is smaller,
we eliminate the left half */
else if(nums[mid] < target) {
low = mid + 1;
}
/* If middle element is greater,
we eliminate the right half */
else {
high = mid - 1;
}
}
return first;
}
// Function to find the last occurrence of the target
int lastOccurrence(vector<int> &nums, int target) {
int low = 0, high = nums.size() - 1;
int last = -1;
// Applying Binary Search Algorithm
while(low <= high) {
int mid = low + (high - low) / 2;
/* If the target element is found, we
update the first variable to mid and
eliminate the right half to look for
more greater index where target is present */
if(nums[mid] == target) {
last = mid;
low = mid + 1;
}
/* If middle element is smaller,
we eliminate the left half */
else if(nums[mid] < target) {
low = mid + 1;
}
/* If middle element is greater,
we eliminate the right half */
else {
high = mid - 1;
}
}
return last;
}
public:
// Function to find the first and last occurrences of the target
vector<int> searchRange(vector<int> &nums, int target) {
// Function call to find the first occurence of target
int first = firstOccurrence(nums, target);
// If the target is not present in the array
if(first == -1) return {-1, -1};
// Function call to find the last occurence of target
int last = lastOccurrence(nums, target);
return {first, last};
}
};
int main()
{
vector<int> nums = {5, 7, 7, 8, 8, 10};
int target = 8;
// Create an instance of the Solution class
Solution sol;
// Function call to find the first and last occurrences
vector<int> result = sol.searchRange(nums, target);
cout << "The first and last occurrences are at indices: "
<< result[0] << " and " << result[1] << "\n";
return 0;
}
import java.util.*;
class Solution {
// Function to find the first occurrence of the target
private int firstOccurrence(int[] nums, int target) {
int low = 0, high = nums.length - 1;
int first = -1;
// Applying Binary Search Algorithm
while(low <= high) {
int mid = low + (high - low) / 2;
/* If the target element is found, we
update the first variable to mid and
eliminate the right half to look for
more smaller index where target is present */
if(nums[mid] == target) {
first = mid;
high = mid - 1;
}
/* If middle element is smaller,
we eliminate the left half */
else if(nums[mid] < target) {
low = mid + 1;
}
/* If middle element is greater,
we eliminate the right half */
else {
high = mid - 1;
}
}
return first;
}
// Function to find the last occurrence of the target
private int lastOccurrence(int[] nums, int target) {
int low = 0, high = nums.length - 1;
int last = -1;
// Applying Binary Search Algorithm
while(low <= high) {
int mid = low + (high - low) / 2;
/* If the target element is found, we
update the first variable to mid and
eliminate the right half to look for
more greater index where target is present */
if(nums[mid] == target) {
last = mid;
low = mid + 1;
}
/* If middle element is smaller,
we eliminate the left half */
else if(nums[mid] < target) {
low = mid + 1;
}
/* If middle element is greater,
we eliminate the right half */
else {
high = mid - 1;
}
}
return last;
}
// Function to find the first and last occurrences of the target
public int[] searchRange(int[] nums, int target) {
// Function call to find the first occurence of target
int first = firstOccurrence(nums, target);
// If the target is not present in the array
if(first == -1) return new int[]{-1, -1};
// Function call to find the last occurence of target
int last = lastOccurrence(nums, target);
return new int[]{first, last};
}
public static void main(String[] args) {
int[] nums = {5, 7, 7, 8, 8, 10};
int target = 8;
// Create an instance of the Solution class
Solution sol = new Solution();
// Function call to find the first and last occurrences
int[] result = sol.searchRange(nums, target);
System.out.println("The first and last occurrences are at indices: "
+ result[0] + " and " + result[1]);
}
}
class Solution:
# Function to find the first occurrence of the target
def firstOccurrence(self, nums, target):
low, high = 0, len(nums) - 1
first = -1
# Applying Binary Search Algorithm
while low <= high:
mid = low + (high - low) // 2
# If the target element is found, we
# update the first variable to mid and
# eliminate the right half to look for
# more smaller index where target is present
if nums[mid] == target:
first = mid
high = mid - 1
# If middle element is smaller,
# we eliminate the left half
elif nums[mid] < target:
low = mid + 1
# If middle element is greater,
# we eliminate the right half
else:
high = mid - 1
return first
# Function to find the last occurrence of the target
def lastOccurrence(self, nums, target):
low, high = 0, len(nums) - 1
last = -1
# Applying Binary Search Algorithm
while low <= high:
mid = low + (high - low) // 2
# If the target element is found, we
# update the first variable to mid and
# eliminate the right half to look for
# more greater index where target is present
if nums[mid] == target:
last = mid
low = mid + 1
# If middle element is smaller,
# we eliminate the left half
elif nums[mid] < target:
low = mid + 1
# If middle element is greater,
# we eliminate the right half
else:
high = mid - 1
return last
# Function to find the first and last occurrences of the target
def searchRange(self, nums, target):
# Function call to find the first occurence of target
first = self.firstOccurrence(nums, target)
# If the target is not present in the array
if first == -1:
return [-1, -1]
# Function call to find the last occurence of target
last = self.lastOccurrence(nums, target)
return [first, last]
if __name__ == "__main__":
nums = [5, 7, 7, 8, 8, 10]
target = 8
# Create an instance of the Solution class
sol = Solution()
# Function call to find the first and last occurrences
result = sol.searchRange(nums, target)
print("The first and last occurrences are at indices:", result[0], "and", result[1])
class Solution {
// Function to find the first occurrence of the target
firstOccurrence(nums, target) {
let low = 0, high = nums.length - 1;
let first = -1;
// Applying Binary Search Algorithm
while(low <= high) {
let mid = low + Math.floor((high - low) / 2);
/* If the target element is found, we
update the first variable to mid and
eliminate the right half to look for
more smaller index where target is present */
if(nums[mid] == target) {
first = mid;
high = mid - 1;
}
/* If middle element is smaller,
we eliminate the left half */
else if(nums[mid] < target) {
low = mid + 1;
}
/* If middle element is greater,
we eliminate the right half */
else {
high = mid - 1;
}
}
return first;
}
// Function to find the last occurrence of the target
lastOccurrence(nums, target) {
let low = 0, high = nums.length - 1;
let last = -1;
// Applying Binary Search Algorithm
while(low <= high) {
let mid = low + Math.floor((high - low) / 2);
/* If the target element is found, we
update the first variable to mid and
eliminate the right half to look for
more greater index where target is present */
if(nums[mid] == target) {
last = mid;
low = mid + 1;
}
/* If middle element is smaller,
we eliminate the left half */
else if(nums[mid] < target) {
low = mid + 1;
}
/* If middle element is greater,
we eliminate the right half */
else {
high = mid - 1;
}
}
return last;
}
// Function to find the first and last occurrences of the target
searchRange(nums, target) {
// Function call to find the first occurence of target
let first = this.firstOccurrence(nums, target);
// If the target is not present in the array
if(first == -1) return [-1, -1];
// Function call to find the last occurence of target
let last = this.lastOccurrence(nums, target);
return [first, last];
}
}
let nums = [5, 7, 7, 8, 8, 10];
let target = 8;
// Create an instance of the Solution class
let sol = new Solution();
// Function call to find the first and last occurrences
let result = sol.searchRange(nums, target);
console.log("The first and last occurrences are at indices:", result[0], "and", result[1]);
Time Complexity: O(log N), where N is the size of the given array. Both the firstOccurrence
and lastOccurrence
functions perform a binary search, which operates in logarithmic time. Thus, the overall time complexity is O(log N).
Space Complexity: O(1), as we are using a constant amount of extra space regardless of the input size. The space used by the variables low
, high
, mid
, first
, and last
does not depend on the size of the input array.
Q: How does the algorithm ensure correctness with duplicates?
A: In a sorted array with duplicates: The first binary search stops only when the leftmost occurrence of the target is found. The second binary search ensures the rightmost occurrence is located by adjusting the search bounds appropriately.
Q: How would you modify the algorithm to find the count of the target value in the array?
A: The count is simply: count=lastIndex−firstIndex+1 This computation is direct and efficient once the first and last occurrences are identified.
Q: Can this algorithm handle floating-point numbers or custom comparison logic?
A: Yes: For floating-point numbers, ensure proper handling of precision in comparisons. For custom logic (e.g., case-insensitive string comparisons), adapt the binary search condition to fit the requirement.