Template left+1 < right: Finding Position of Target
Example Given nums = [1, 2, 2, 5, 7, 7].
For target = 2, return 1 or 2.
For target = 5, return 3.
For target = 6, return -1.
Template
1 |
|
Code
1 |
|
Template left+1 < right: Find First Position of Target
Example Given nums = [1, 2, 2, 5, 7, 7].
For target = 2, return 1.
For target = 7, return 4.
For target = 6, return -1.
Template
1 |
|
Code
1 |
|
Version 2:
1 |
|
Binary Search Template
Binary Search can solve many problems that are asking for finding a particular element, and it has time complexity of $O(\log n)$.
In most cases, if a problem is asking the following, we should think about using Binary Search.
- The array we are going to search is sorted or partial sorted.
- If required time complexity is less than $O(n)$, or required $O(\log n)$.
Binary Search has many various forms. We will discuss the following:
- Standard Binary Search
- Find the target’s first position
- Find the target’s last position
- Find the target’s first and last position
- Find any peek element
Standard Binary Search
First, we will explain the standard Binary Search template.
1 |
|
- Loop condition:
left <= right
- Mid position:
mid = left + ((right -left) >> 1)
- Left position update:
left = mid + 1
- Right position update:
right = mid - 1
- Return value:
mid
or-1
Note:
- Our loop condition includes
left == right
, so in every iteration, we should updateleft
andright
position so that prevent infinity loop. - Loop ending condition:
- Found the target
left > right
(This situation will happen ifleft
,right
,mid
are pointing to the same position, but the value is not equal to target and we finish finding the entire array)
left + ((right -left) >> 1)
is equal to(left + right) / 2
. We prefer the first form because it can prevent(left + right) > Integer.MAX_VALUE
. We use right shift so the performance will improve.- If the number of array is odd,
left + ((right -left) >> 1)
will point to the middle element; if even, it will point to left middle. Therefore, it will have below two conditions whenleft
andright
meet.left
/mid
,right
(left and mid point to the same element, right point to its next element)left/mid/right
point to the same element For example, in array[1, 2]
,left
will point to1
, same asleft
.
Practice 1: Guess Number Higher or Lower
- LeetCode 374. Guess Number Higher or Lower
We are playing the Guess Game. The game is as follows:
I pick a number from 1 to n. You have to guess which number I picked.
Every time you guess wrong, I’ll tell you whether the number is higher or lower.
You call a pre-defined API guess(int num)
which returns 3 possible results (-1
, 1
, or 0
):
1 |
|
Example :
1 |
|
We can use standard Binary Search to solve this problem.
1 |
|
Practice 2: Sqrt(x)
- LeetCode 69. Sqrt(x)
Implement int sqrt(int x)
.
Compute and return the square root of $x$, where $x$ is guaranteed to be a non-negative integer.
Since the return type is an integer, the decimal digits are truncated and only the integer part of the result is returned.
Example 1:
1 |
|
Example 2:
1 |
|
It seems like this problem is not related to Binary Search, but we can actually use Binary Search to find a guess number, then compare if we should continue finding the left side of the guess number or right side of the guess number. For example, if we want to find the square root of 16
. If we first guess 8, then compare 8
and 16/8=2
, since 8 > 2
, we know the target will be on the left side of 8
. If we guess 5, then compare 3
and 16/3=5
, since 3 < 5
, we know the target will be on the right side of 3
. If we guess 4
, since 4 = 16/4
, then we know 4
is the target.
1 |
|
In the above problem, we use mid > x / mid
instead of mid * mid > x
because we want to prevent out of mid * mid > Integer.MAX_VALUE
. This is the same reason why we use left + ((right - left) >> 1)
but not (left + right) / 2
.
Find First Position of Target
We use Binary Search to find the first position of target, or the most left position of target if the problem include any one of the following requirement:
- Array is sorted, and array has duplicated number
- Array is partial sorted, and array does not have duplicated number
- Array is partial sorted, and array has duplicated number
Find First Position of Target Type 1
This type template solves the above first and second situations.
Since we are finding the most left position of the target, then theright
must moving to the left side. In other words, if we find out nums[mid] == target
, this mid
position may not be the most left position of the target, we still neeed to find the left side. So if nums[mid] > target
or nums[mid] == target
, we are moving right
to mid
, in other words, toward the left side.
1 |
|
- Loop condition:
left < right
- Mid position:
mid = left + ((right -left) >> 1)
- Left position update:
left = mid + 1
- Right position update:
right = mid
- Return value:
nums[left] == target ? left : -1
This is different from the standard Binary Search:
First, the right position update is right = mid
because when we already found the target, we want to keep find the left side of mid
if other values equal to target on the left side of mid
.
Next, the loop condition is left < right
. Because at the end if left
and right
are next to each other, then mid
and left
will be point to the same position. Then next iteration, left
, mid
, and right
will point to the same position. If the loop condition is changed to left <= right
, then we need will have next iteration. If nums[mid] < target
, then we are good, out of this loop. Else, we will have right = mid
, and we didn’t change any of left
, mid
, right
so we will have an infinity loop.
In fact, we only loop till left
and right
are next to each other. Because after this iteration, left
, mid
, and right
will point to the same position. If this position’s value is equal to the target, then this position must be the most left position of the target. If it doesn’t equal to target, then it means we do not find the target in the whole array. Therefore, we have nums[left] == target ? left : -1
outside the loop.
Practice 3: First Bad Version
- LeetCode 278. First Bad Version
You are a product manager and currently leading a team to develop a new product. Unfortunately, the latest version of your product fails the quality check. Since each version is developed based on the previous version, all the versions after a bad version are also bad.
Suppose you have n
versions [1, 2, ..., n]
and you want to find out the first bad one, which causes all the following ones to be bad.
You are given an API bool isBadVersion(version)
which will return whether version
is bad. Implement a function to find the first bad version. You should minimize the number of calls to the API.
Example:
1 |
|
Above is the problem that asking us to find the most left position of the target. We can use the template directly.
1 |
|
Similar examples also include 744. Find Smallest Letter Greater Than Target and 658. Find K Closest Elements.
Practice 4: Find Minimum in Rotated Sorted Array
Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.
(i.e., [0,1,2,4,5,6,7]
might become [4,5,6,7,0,1,2]
).
Find the minimum element.
You may assume no duplicate exists in the array.
Example 1:
1 |
|
Example 2:
1 |
|
This problem is the second situation. Array is partial sorted, and array does not have duplicated number. We can think of it asking us to find the original array’s most left
position value.
1 |
|
Find First Position of Target Type 2
This method is used in the third situation that array is partial sorted, and array has duplicated number. In this situation, when we making the right
toward the left side, we can’t simply do right = mid
when nums[mid] == target
because there are duplicated numbers, and it will skip some numbers from mid+1 to right-1
inclusive. So, we when nums[mid] == target
, we will move right
only one step forward, in other words, right = right-1
when nums[mid] == target
.
1 |
|
Practice 5: Find Minimum in Rotated Sorted Array II
Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.
(i.e., [0,1,2,4,5,6,7]
might become [4,5,6,7,0,1,2]
).
Find the minimum element.
The array may contain duplicates.
Example 1:
1 |
|
Example 2:
1 |
|
Note:
- This is a follow up problem to Find Minimum in Rotated Sorted Array.
- Would allow duplicates affect the run-time complexity? How and why?
This problem is similar to Practice 4, but with one more condition that array may contains duplicted elements. We can use the find first position of target type 2 to solve it.
1 |
|
Find Last Position of Target
1 |
|
- Loop condition:
left < right
- Mid position:
mid = left + ((right -left) >> 1) + 1
- Left position update:
left = mid
- Right position update:
right = mid - 1
- Return value:
nums[right] == target ? right : -1
This is symmetrically written as finding the first position of target. Note that here the mid
position calculation is changed, we are adding 1 behind. Now, no matter the array has odd or even number, the mid
is on the right side of the middle.
When we find the most left position of the target, the mid
is on the left side of the middle; if we find the most right side position of the target, the mid
is on the right side of the middle. When left
and right
next to each other, if mid
is on the left side of the middle, then left
and mid
point to the same position, right
point to their next position. If we do not plus one at the end of the mid
, in other words, the mid
is on the left side of the middle, and if nums[left] == target
, then the left
, right
, mid
never change and we are in the infinity loop. Therefore, we want the mid
to be on the right side of the middle, then the left
can move toward the right.
Find First and Last Position of Target
We can first find the left most position of the target, then find the right most position of the target.
Practice 6: Find First and Last Position of Element in Sorted Array
Given an array of integers nums
sorted in ascending order, find the starting and ending position of a given target
value.
Your algorithm’s runtime complexity must be in the order of $O(\log n)$.
If the target is not found in the array, return [-1, -1]
.
Example 1:
1 |
|
Example 2:
1 |
|
1 |
|
Find Peek Element
This type of problem is comparing the left neighbor and right neightbor.
Practice 7: Find Peak Element
- LeetCode 162. Find Peak Element
A peak element is an element that is greater than its neighbors.
Given an input array nums
, where nums[i] ≠ nums[i+1]
, find a peak element and return its index.
The array may contain multiple peaks, in that case return the index to any one of the peaks is fine.
You may imagine that nums[-1] = nums[n] = -∞
.
Example 1:
1 |
|
Example 2:
1 |
|
Note:
Your solution should be in logarithmic complexity.
This problem asks us to find any of the local peek element, and there’s no duplicated element, and the array even is not sorted. We can solve it by comparing nums[mid] < nums[mid + 1]
.
1 |
|
Note that in this problem, we are comparing nums[mid] < nums[mid + 1]
, in fact, it’s checking the neighbor elements’s characteristic.
Find first greater element in a sorted array
Practice 8: Find first greater element in a sorted array
1 |
|
Find first smaller element in a sorted array
Practice 9: Find first smaller element in a sorted array
1 |
|
Summary
Template | Loop Condition | Left Position Update | Right Position Update | Calculating Middle | Return Value |
---|---|---|---|---|---|
Standard | left <= right |
left = mid + 1 |
right = mid - 1 |
(left + right) / 2 |
mid / -1 |
Most Left Position | left < right |
left = mid + 1 |
right = mid |
(left + right) / 2 |
left / -1 |
Most Right Position | left < right |
left = mid |
right = mid - 1 |
(left + right) / 2 + 1 |
right / -1 |
Source: