
Problem Statement
The task is centralized around finding the starting and ending position of a specific value referred to as target
within a sorted array nums
. This array is characterized by its elements being sorted in non-decreasing order. When target
is found, the algorithm should return the indices of its first and last appearance within the array. However, if the target
does not exist in the array, the algorithm should return [-1, -1]
. The computation needs to be efficient, adhering to a time complexity of O(log n), indicating that a straightforward linear search is insufficient due to potential scalability issues with larger arrays.
Examples
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
Approach and Intuition
Understand the Problem Using Examples
Let's illustrate the problem and solution approach through the provided examples:
- For the input
nums = [5,7,7,8,8,10]
andtarget = 8
, the target number 8 appears at indices 3 and 4. Thus the output is[3,4]
. - In the second example with
nums = [5,7,7,8,8,10]
andtarget = 6
, the number 6 does not exist in the array. Consequently, the output is[-1,-1]
. - In the third example, given an empty array
nums = []
and anytarget
, there are no elements to search through, resulting in a return of[-1,-1]
.
Breaking Down the Approach
Since the problem requires an O(log n) solution, binary search presents itself as a suitable method due to its logarithmic time complexity. This involves:
- Performing a modified binary search to find the leftmost (first occurrence) of the target value.
- Performing another modified binary search to find the rightmost (last occurrence) of the same target.
- Initially set two pointers, one pointing to the start (
left
) and the other to the end (right
) of the array. - While the
left
pointer is less than or equal to theright
pointer:- Calculate the middle position.
- If the middle element matches the target, expand outwards to ensure it's the first or last occurrence.
- If the middle element is less than the target, narrow the search to the right half.
- If the middle element is more than the target, narrow the search to the left half.
- Exit the loop with the positions of the first and last occurrence or determine that the target is not present.
This approach leverages the sorted nature of the array, using the efficiency of binary search to quickly zoom in on the required indices or conclude the absence of the target.
Solutions
- C++
- Java
- C
- JavaScript
- Python
class Solution {
public:
vector<int> findFirstAndLast(vector<int>& nums, int target) {
int lowerBound = findPosition(nums, target, true);
if (lowerBound == -1) {
return vector<int>{-1, -1};
}
int upperBound = findPosition(nums, target, false);
return vector<int>{lowerBound, upperBound};
}
private:
int findPosition(vector<int>& nums, int value, bool findFirst) {
int size = nums.size();
int start = 0, end = size - 1;
while (start <= end) {
int middle = (start + end) / 2;
if (nums[middle] == value) {
if (findFirst) {
if (middle == start || nums[middle - 1] != value) {
return middle;
}
end = middle - 1;
} else {
if (middle == end || nums[middle + 1] != value) {
return middle;
}
start = middle + 1;
}
} else if (nums[middle] > value) {
end = middle - 1;
} else {
start = middle + 1;
}
}
return -1;
}
};
This solution in C++ provides an efficient method to find the first and last position of a given element in a sorted array. The algorithm effectively utilizes the concept of binary search to locate the desired positions, ensuring a time complexity of O(log n), thereby optimizing the search process in large datasets.
The process encapsulated in the findFirstAndLast
function involves:
- Determining the lower bound of the target using the
findPosition
function withfindFirst
set to true. If the target isn't found (indicated by-1
), the function instantly returns{-1, -1}
. - If the lower bound is found, it proceeds to find the upper bound, again using the
findPosition
function but withfindFirst
set to false.
The findPosition
function incorporates a binary search technique, adjusting start and end indexes based on comparisons with the target:
- If
findFirst
is true and the target matchesnums[middle]
, the function checks if it's the first occurrence. Ifmiddle
matchesstart
or the previous element is not the target, the position is confirmed. - If looking for the last occurrence (
findFirst
false), similar logic applies but checks if the found position is the last occurrence of the target. - The usual binary search adjustments of either narrowing down from the start or the end of the array continue until the target is located or until the search space is exhausted.
The encapsulation into a private function for the repetitive process of locating positions reduces redundancy and enhances clarity, making the code modular and easier to troubleshoot. This implementation ensures robust handling of edge cases such as not finding the target or the target existing at the boundaries of the array. All operations focus on efficiency and clarity, adhering closely to the principles of structured, modular design.
class Solution {
public int[] findFirstAndLastPosition(int[] numbers, int targetValue) {
int lowerBound = this.determinePosition(numbers, targetValue, true);
if (lowerBound == -1) {
return new int[] { -1, -1 };
}
int upperBound = this.determinePosition(numbers, targetValue, false);
return new int[] { lowerBound, upperBound };
}
private int determinePosition(int[] numbers, int targetValue, boolean isLower) {
int length = numbers.length;
int start = 0, finish = length - 1;
while (start <= finish) {
int midPoint = (start + finish) / 2;
if (numbers[midPoint] == targetValue) {
if (isLower) {
if (midPoint == start || numbers[midPoint - 1] != targetValue) {
return midPoint;
}
finish = midPoint - 1;
} else {
if (midPoint == finish || numbers[midPoint + 1] != targetValue) {
return midPoint;
}
start = midPoint + 1;
}
} else if (numbers[midPoint] > targetValue) {
finish = midPoint - 1;
} else {
start = midPoint + 1;
}
}
return -1;
}
}
This Java solution defines a method to find the first and last positions of a given element in a sorted array. It uses binary search to efficiently locate the positions, optimizing the search process when the element is found or confirming its absence.
- The method
findFirstAndLastPosition
initializes the search for both bounds using another methoddeterminePosition
. - The
determinePosition
method conducts the binary search. By passing a boolean parameter, it adjusts the search dynamically for the lower or upper bound.
Detailed Explanation of the Method:
- Start by initializing
lowerBound
usingdeterminePosition
method configured to find the lower boundary. - If
lowerBound
is-1
(target value not found), immediately return{-1, -1}
. - If the target value is present,
upperBound
is determined using the samedeterminePosition
method configured for finding the upper boundary. - Finally, return the positions as an array:
{lowerBound, upperBound}
.
Within determinePosition
:
- The method establishes a binary search loop adjusting
start
andfinish
pointers based on comparisons. - Adjust the pointers based on whether the search is for the lower or upper bound when the target is equal to the middle value:
- For the lower bound, verify that the preceding item is different.
- For the upper bound, check that the following item is not the target.
- Adjust the respective pointers accordingly (
start
orfinish
) to narrow the search.
- Continue adjusting the boundaries (
start
andfinish
) until the target is found or the bounds invalidate the loop. - Return either the position of the target or
-1
if it isn't found during the adjustments.
This efficient binary search approach ensures the solution remains optimal even for large arrays, with its runtime complexity being O(log n) due to the binary search properties.
int locatePosition(int* array, int arrayLength, int value, int isLowest) {
int start = 0, finish = arrayLength - 1;
while (start <= finish) {
int middle = (start + finish) / 2;
if (array[middle] == value) {
if (isLowest) {
if (middle == start || array[middle - 1] != value) {
return middle;
}
finish = middle - 1;
} else {
if (middle == finish || array[middle + 1] != value) {
return middle;
}
start = middle + 1;
}
} else if (array[middle] > value) {
finish = middle - 1;
} else {
start = middle + 1;
}
}
return -1;
}
int* searchExtremes(int* array, int arrayLength, int value, int* returnedSize) {
int* extremities = malloc(sizeof(int) * 2);
int lowestOccurrence = locatePosition(array, arrayLength, value, 1);
if (lowestOccurrence == -1) {
extremities[0] = -1;
extremities[1] = -1;
*returnedSize = 2;
return extremities;
}
int highestOccurrence = locatePosition(array, arrayLength, value, 0);
extremities[0] = lowestOccurrence;
extremities[1] = highestOccurrence;
*returnedSize = 2;
return extremities;
}
The provided C code defines a solution for finding the first and last positions of a specific element in a sorted array. The solution utilizes two primary functions:
locatePosition
: This function is designed to find either the lowest or highest index of the specified element (value
) in the array. The function takes the completearray
, itsarrayLength
, thevalue
to find, and a booleanisLowest
to determine if you are looking for the lowest or highest index.- It initializes
start
to the beginning of the array andfinish
to the end. - A binary search is performed within the while loop. Depending on whether
isLowest
is true or false, the index gets adjusted to either keep searching to the left (to find the first occurrence) or right (to find the last occurrence). - If the element is found at the calculated mid index and conditions for being the lowest or highest are met, it returns the index.
- If the desired element is not found in the range, it returns -1.
- It initializes
searchExtremes
: This function utilizes thelocatePosition
function to find both the first and last occurrences of an element.- It initially allocates memory for an array
extremities
to store the results. - Calls
locatePosition
twice withisLowest
set to true for the first call and false for the second call, effectively finding the lowest and highest occurrences ofvalue
. - Stores these indices in
extremities
. - Sets
returnedSize
(likely intended as output to indicate the size ofextremities
) to 2. - Returns
extremities
, which contains the first and last positions of the element. If the element is not found at all, both positions are set to -1.
- It initially allocates memory for an array
This solution effectively implements a modified binary search and memory management using malloc
, making it efficient for large arrays. Moreover, it robustly handles cases where the target value might not be present in the array. This approach is key in scenarios where performance is critical, and handling non-existent values gracefully is essential.
var findTargetRange = function (elements, targetValue) {
const initialIndex = locateBound(elements, targetValue, true);
if (initialIndex == -1) {
return [-1, -1];
}
const finalIndex = locateBound(elements, targetValue, false);
return [initialIndex, finalIndex];
function locateBound(elements, targetValue, isStarting) {
let length = elements.length;
let start = 0,
end = length - 1;
while (start <= end) {
let middle = Math.floor((start + end) / 2);
if (elements[middle] == targetValue) {
if (isStarting) {
if (middle == start || elements[middle - 1] != targetValue) {
return middle;
}
end = middle - 1;
} else {
if (middle == end || elements[middle + 1] != targetValue) {
return middle;
}
start = middle + 1;
}
} else if (elements[middle] > targetValue) {
end = middle - 1;
} else {
start = middle + 1;
}
}
return -1;
}
};
The JavaScript function findTargetRange
finds the first and last positions of a specified element within a sorted array. This is particularly useful when dealing with duplicates in the array and needing to ascertain the range of indices for a specific target value.
Following is a breakdown of the process:
Use the function
locateBound
for locating the desired indices. This nested function accepts three parameters:elements
for the array,targetValue
for the element to find, andisStarting
to determine if searching for the start or end index.The
locateBound
function initializes withstart
andend
set to the boundaries of the array and employs a binary search mechanism. If the middle element matches thetargetValue
:When searching for the start index (
isStarting
is true), check if it is the first occurrence oftargetValue
. If not, adjust theend
to tighten the search space.When searching for the end index (
isStarting
is false), verify if the found index is the last occurrence. If not, adjust thestart
to refine the search range.
The function
findTargetRange
initially invokeslocateBound
to search for the starting index. If no such index exists, it returns[-1, -1]
indicating the absence oftargetValue
.If the starting index is found,
locateBound
is called again to locate the final index, using the previously located starting point.
This approach ensures an efficient search with a complexity of O(log n), which is optimal for sorted arrays. The resultant indices of the first and last occurrence of the target element are then returned as an array. This detailed and precise method leverages binary search principles tailored for sorted arrays, making it efficient and effective for frequent and varied applications.
class Solution:
def locateRange(self, arr: List[int], key: int) -> List[int]:
first_index = self.findIndex(arr, key, True)
if first_index == -1:
return [-1, -1]
last_index = self.findIndex(arr, key, False)
return [first_index, last_index]
def findIndex(self, arr: List[int], key: int, isStart: bool) -> int:
length = len(arr)
low, high = 0, length - 1
while low <= high:
mid = int((low + high) / 2)
if arr[mid] == key:
if isStart:
if mid == low or arr[mid - 1] < key:
return mid
high = mid - 1
else:
if mid == high or arr[mid + 1] > key:
return mid
low = mid + 1
elif arr[mid] > key:
high = mid - 1
else:
low = mid + 1
return -1
The Python solution aims to find the first and last position of a given element in a sorted array. The solution comprises two main methods within the Solution
class:
locateRange
: This method calls the helper functionfindIndex
twice. Once to find the first occurrence of the key and once to find the last occurrence. If thefindIndex
method returns-1
for the first occurrence, the function immediately returns[-1, -1]
, indicating that the key is not present. If the key exists, it proceeds to find the last index.findIndex
: This method uses a binary search approach to find an index based on whether the search is for the first or last occurrence of the key identified by theisStart
boolean. WhenisStart
is true, the function searches for the first occurrence of the key. IfisStart
is false, the function looks for the last occurrence. If the key is found based on the condition specified (first or last), the respective index is returned. If the key doesn't exist at all, the method returns-1
.
Here's what happens step-by-step:
- Define the
locateRange
method to manage the flow and handle cases where the key might not be present. - Define
findIndex
method to apply a binary search for detecting either the first or last index of the key. - Apply conditions in the binary search logic to ensure the correct occurrence (first or last) is accurately determined and returned.
By integrating binary search, the solution efficiently narrows down the search space, ensuring a time complexity of O(log n), which is optimal for sorted arrays.
This approach emphasizes clarity and efficiency by segregating the search logic into a dedicated method (findIndex
) and using locateRange
to handle the search outcome and edge cases. This modular structure also makes the code easier to manage and debug.
No comments yet.