Difficulty: Easy, Asked in: Google, Amazon, Microsoft.
Key takeaway: An excellent problem to learn problem-solving using binary search. Here we apply binary search twice to improve the time complexity.
Given an array of integers sorted in ascending order, write a program to find the first and last position of a given target value.
Input: A[] = [-1, 1, 2, 2, 2, 5, 6, 12, 12], target = 2.
Output: First occurrence = 2, Last occurrence = 4.
Input: A[] = [21, 32, 51, 70, 71], target = 70.
Output: First occurrence = 3, Last occurrence = 3.
One basic solution is to traverse the array linearly and track the index of the first and last occurrence of the target. This approach is inefficient because we did not take advantage of the sorted order property.
firstLastOccurrence(A, n, target)
firstLast = [-1, -1]
// Scan the array to find the first and last positions
for i from 0 to n - 1
if A[i] == target
if firstLast[0] == -1
firstLast[0] = i
firstLast[1] = i
return firstLastWe are running a single loop n time and doing O(1) operations at each iteration. Time complexity = n * O(1) = O(n). We are using constant extra space, so space complexity = O(1).
Now the critical questions are: Can we improve the time complexity further? Can we use the sorted order property to enhance the efficiency of searching? Let's think!
As we know, binary search will take O(logn) time to search the target value in the sorted array. But the given problem is different from the binary search problem because we need to search for two occurrences of the target element. So, how can we modify the standard binary search algorithm to solve it?
The idea is to use binary search twice. We use the first binary search to find the first occurrence of the target, and the second binary search to find the last occurrence.
Let's use the iterative binary search! We first initialize low = 0 and high = n - 1 to track the left and right ends. Now we run a loop till low ≤ high:
findFirstOccurrence(A, n, target)
low = 0
high = n - 1
// Search for the first position containing target
while low <= high
mid = low + (high - low) / 2
if A[mid] == target and (mid == 0 or A[mid - 1] < target)
return mid
else if A[mid] < target
low = mid + 1
else
high = mid - 1
return -1Similarly, to find the last occurrence, we also modify the binary search algorithm. For this, we first initialize low = 0 and high = n - 1 to track the left and right ends. Now we run a loop until low ≤ high:
findLastOccurrence(A, n, target)
low = 0
high = n - 1
// Search for the last position containing target
while low <= high
mid = low + (high - low) / 2
if A[mid] == target and (mid == n - 1 or A[mid + 1] > target)
return mid
else if A[mid] > target
high = mid - 1
else
low = mid + 1
return -1findLastOccurrence(A, n, target)
low = 0
high = n - 1
// Search for the last position containing target
while low <= high
mid = low + (high - low) / 2
if A[mid] == target and (mid == n - 1 or A[mid + 1] > target)
return mid
else if A[mid] > target
high = mid - 1
else
low = mid + 1
return -1
findFirstOccurrence(A, n, target)
low = 0
high = n - 1
// Search for the first position containing target
while low <= high
mid = low + (high - low) / 2
if A[mid] == target and (mid == 0 or A[mid - 1] < target)
return mid
else if A[mid] < target
low = mid + 1
else
high = mid - 1
return -1
findFirstLastOccurrence(A, n, target)
firstLast = [-1, -1]
firstLast[0] = findFirstOccurrence(A, n, target)
firstLast[1] = findLastOccurrence(A, n, target)
return firstLastWe are applying binary search twice. Time complexity = Time complexity to search first occurrence + Time complexity to search last occurrence = O(1) + O(logn) + O(logn) = O(log n). We are using the implementation of iterative binary search, which takes O(1) extra space. Space complexity = O(1)
Thanks to Navtosh for his contribution in creating the first version of this content. If you have any queries or feedback, please write us at contact@enjoyalgorithms.com. Enjoy learning, Enjoy algorithms!