Welcome back! In our last lesson, we built a solid foundation with binary search and its powerful variations, lower_bound and upper_bound. We saw how these tools are not just about finding an element, but about understanding its position within a sorted structure.
Today, we'll put that knowledge to immediate practical use. We're going to tackle a classic interview problem: finding the first and last positions of an element in a sorted array that contains duplicates. This problem directly tests your understanding of the nuances of binary search. You'll see how the concepts of lower_bound and upper_bound provide an elegant solution, and we'll also build the solution from scratch using modified binary searches to ensure you're prepared for any interview question style.
By the end of this lesson, you will be able to apply binary search to efficiently find the starting and ending indices of a target value, progressing from a simple linear scan to two distinct and optimal logarithmic-time solutions.

1. The Brute-Force Approach: Linear Scan
Following our established method, let's start with the most straightforward approach. Given the task of finding the first and last occurrence, the simplest idea is to just walk through the array.
We can use a single loop to iterate from the beginning to the end of the array. We would maintain two variables, say first and last, both initialized to -1.
- The very first time we encounter the target element, we set
firstto the current index. - Every time we see the target element, we update
lastwith the current index.
After the loop completes, first will hold the index of the first occurrence, and last will hold the index of the last one.
BS-3. First and Last Occurrences in Array | Count occurrences in Array
The "take U forward" video begins by explaining this exact brute-force strategy.
Watch the initial segment from the explanation to understand the linear search logic and its time complexity.
This approach is correct, but its time complexity is . The moment you hear "sorted array" and "search", you should immediately think of binary search. An solution for a sorted array search problem is almost always a signal that a more optimal, logarithmic solution exists.
2. Optimal Approach 1: Leveraging lower_bound and upper_bound
This is where our work from the previous lesson pays off. Let's recall the definitions:
lower_bound(x): Returns the index of the first element that is greater than or equal tox.upper_bound(x): Returns the index of the first element that is strictly greater thanx.
How can we use these to find the first and last occurrences of our target x?
- First Occurrence: The definition of
lower_bound(x)is exactly what we need for the first occurrence. It gives us the index of the first element that is>= x. If this element is indeedx, we've found our first occurrence. - Last Occurrence: This is a bit more clever.
upper_bound(x)gives us the index of the first element that is> x. The element just before this must therefore be the last element that is<= x. So, the index of the last occurrence ofxis simplyupper_bound(x) - 1.
This gives us a very elegant two-step solution:
- Calculate
first = lower_bound(x). - Calculate
last = upper_bound(x) - 1.
Of course, we must handle the edge case where the element x is not in the array. We can do this by checking the result of our lower_bound call. If the index it returns is out of bounds, or if the element at that index is not our target x, then the element does not exist in the array, and we should return [-1, -1].
BS-3. First and Last Occurrences in Array | Count occurrences in Array
Striver's video provides an excellent explanation of this pattern-based solution.
Watch the segment from this timestamp to see how lower_bound and upper_bound are used to solve the problem, including the critical edge case check.
This approach is highly efficient, with a time complexity of because it just involves two binary search operations. It's a powerful demonstration of how reusing well-understood patterns (lower_bound, upper_bound) can lead to clean and optimal solutions.
Here's how this would look in Java, using the lowerBound and upperBound functions we developed in the previous lesson:
public int[] searchRange(int[] nums, int target) {
int lb = lowerBound(nums, target);
// Check if the target exists
if (lb == nums.length || nums[lb] != target) {
return new int[]{-1, -1};
}
int ub = upperBound(nums, target);
return new int[]{lb, ub - 1};
}
3. Optimal Approach 2: Two Modified Binary Searches
While the lower_bound/upper_bound method is elegant, some interviewers might ask you to implement the logic from scratch without relying on helper functions. This requires modifying the standard binary search algorithm. We'll need to run it twice: once to find the first position, and once to find the last.
Finding the First Occurrence
The goal is to find the leftmost index of the target.
- Perform a standard binary search.
- If
arr[mid] == target, we have found a potential answer. We store this index, say in a variablefirst = mid. However, there might be another occurrence of the target to the left. So, we don't stop. We continue searching in the left half by settinghigh = mid - 1. - If
arr[mid] < target, we search the right half (low = mid + 1). - If
arr[mid] > target, we search the left half (high = mid - 1).
By repeatedly trying to search leftward after finding a match, we ensure that our first variable will eventually hold the index of the leftmost occurrence.
Finding the Last Occurrence
The logic is symmetric. The goal is to find the rightmost index.
- Perform another binary search.
- If
arr[mid] == target, we have found a potential answer. We store this index, say inlast = mid. But there might be more occurrences to the right. So, we continue searching in the right half by settinglow = mid + 1. - The rest of the binary search logic remains the same.
This ensures our last variable will hold the index of the rightmost occurrence.
The following video provides a fantastic, step-by-step walkthrough of this two-pass binary search approach in Java.
First And Last Position Of An Element In A Sorted Array | FREE DSA Course in JAVA | Lecture 54
This video from TAP ACADEMY is dedicated to solving this problem in Java using two modified binary searches.
Finding First Position: Watch from this segment to see how binary search is modified to find the first occurrence. Pay close attention to how the high pointer is adjusted when a match is found. Finding Last Position: Continue from this part, where the logic is adapted to find the last occurrence by adjusting the low pointer after a match.
For a full implementation in Java, you can study the following article from takeuforward. It presents the complete code with helper functions for firstOccurrence and lastOccurrence.
Count Occurrences in Sorted Array - Tutorial - takeUforward
This article, while titled "Count Occurrences," is based on finding the first and last position. It provides a clear algorithm and complete Java code for the two-pass binary search method.
First, read the Optimal Approach algorithm. It describes the strategy of finding the first and last occurrences separately. Then, carefully study the Java code implementation. The firstOccurrence and lastOccurrence methods perfectly implement the logic we just discussed.
4. Application: Counting Occurrences
A very common follow-up question is: "Now, can you count the total number of times the target appears in the array?"
Once you have the first and last occurrences, this becomes trivial. If first is the index of the first occurrence and last is the index of the last one, the total count is simply:
If the element was not found (first == -1), the count is 0. This simple formula is the reason why finding the first and last positions is such a foundational problem.
BS-3. First and Last Occurrences in Array | Count occurrences in Array
Striver's video concludes by showing how this problem directly solves the "count occurrences" problem.
Watch the short final segment from this timestamp to see the connection.
Conclusion
Today, we've dissected a cornerstone binary search problem. We explored three distinct paths to the solution: a simple but inefficient linear scan, an elegant and optimal solution using lower_bound and upper_bound, and a fundamental implementation using two modified binary searches. For interviews, it's crucial to understand both optimal approaches.
Key Takeaways:
- Problem: Find the start and end index of a target in a sorted array with duplicates.
- Brute-Force: An linear scan can find the first and last indices but is inefficient.
- Optimal Approach 1 (Pattern-based): Use
lower_boundto find the first occurrence andupper_bound - 1to find the last. This is an solution that relies on pre-existing patterns. - Optimal Approach 2 (Implementation-based): Run two modified binary searches. The first search continues leftward on a match to find the first occurrence. The second search continues rightward on a match to find the last. This is also an solution.
- Application: The number of occurrences can be calculated as
last_index - first_index + 1.
In our next lesson, we'll continue to stretch our binary search skills by tackling another classic variation: searching in a rotated sorted array. This will challenge us to find a way to apply binary search even when the array isn't perfectly sorted from end to end.