Binary Search
Binary Search
Topics :
Let's play a little game to give you an idea of how different algorithms for the
same problem can have wildly different efficiencies. The computer is going to
randomly select an integer from 1 to 15. You'll keep guessing numbers until you
find the number, and the we will tell you each time if your guess was too high or
too low :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Once you've found the number, reflect on what technique you used when deciding
what number to guess next.
Maybe you guessed 1, then 2, then 3, then 4, and so on, until you guessed the right
number. We call this approach linear search, because you guess all the numbers as
if they were lined up in a row. It would work. But what is the highest number of
guesses you could need ? If the we selects 15, you would need 15 guesses. Then
again, you could be really lucky, which would be when the we selects 1 and you get
the number on your first guess. How about on average? If we likely to select any
number from 1 to 15, then on average you'll need 8 guesses.
But you could do something more efficient than just guessing 1, 2, 3, 4, …, right ?
Since we tells you whether a guess is too low, too high, or correct, you can start
off by guessing 8. If the number that the computer selected is less than 8, then
because you know that 8 is too high, you can eliminate all the numbers from 8 to
15 from further consideration. If the number selected by the computer is
greater than 8, then you can eliminate 1 through 8. Either way, you can eliminate
half the numbers. On your next guess, eliminate half of the remaining numbers.
Keep going, always eliminating half of the remaining numbers.
We call this halving approach binary search, and no matter which number from 1
to 15 the computer has selected, you should be able to find the number in at most
4 guesses with this technique.
In this algorithm,
• Divide the search space into two halves by finding the middle index "mid".
• Compare the middle element of the search space with the key.
• If the key is found at middle element, the process is terminated.
• If the key is not found at middle element, choose which half will be used as
the next search space.
• If the key is smaller than the middle element, then the left side is used
for next search.
• If the key is larger than the middle element, then the right side is used
for next search.
• This process is continued until the key is found or the total search space is
exhausted.
First Step : Calculate the mid and compare the mid element with the key. If the
key is less than mid element, move to left and if it is greater than the mid then
move search space to the right.
• Key (i.e., 23) is greater than current mid element (i.e., 16). The search space
moves to the right.
• Key is less than the current mid 56. The search space moves to the left.
Second Step : If the key matches the value of the mid element, the element is
found and stop search.
How to Implement Binary Search ?
The Binary Search Algorithm can be implemented in the following two ways
• Iterative Binary Search Algorithm
• Recursive Binary Search Algorithm
Here we use a while loop to continue the process of comparing the key and
splitting the search space in two halves.
Create a recursive function and compare the mid of the search space with the
key. And based on the result either return the index where the key is found or
call the recursive function for the next search space.
Complexity Analysis :
• Time Complexity :
• Auxiliary Space :
O(1), If the recursive call stack is considered then the auxiliary space will be
O(logN).
• Binary search is faster than linear search, especially for large arrays.
• More efficient than other searching algorithms with a similar time
complexity, such as interpolation search or exponential search.
• Binary search is well-suited for searching large datasets that are stored in
external memory, such as on a hard drive or in the cloud.
• Binary search can be used as a building block for more complex algorithms
used in machine learning, such as algorithms for training neural networks or
finding the optimal hyperparameters for a model.
• It can be used for searching in computer graphics such as algorithms for ray
tracing or texture mapping.
• It can be used for searching a database.
BINARY SEARCH ( LEETCODE-704 )
03 March 2024 19:41
Example 1 :
Example 2 :
Constraints :
Link : https://leetcode.com/problems/binary-search/description/
Approach – 01 :
Algorithm / Intuition :
We will use a couple of pointers i.e. low and high to apply binary search. Initially,
the low pointer should point to the first index and the high pointer should point to
the last index.
Search space : The entire area between the low and the high pointer(including
them) is considered the search space. Here, the search space is sorted.
Algorithm :
Now, we will apply the binary search algorithm in the given array:
The above steps will continue until either we found the target or the search space
becomes invalid i.e. high < low. By definition of search space, it will lose its
existence if the high pointer is appearing before the low pointer.
Dry-run :
Note: If the target is not present in the array, low and high will cross each
other.
Note: For a better understanding, please watch the video at the bottom of the
page.
Iterative implementation :
• Initially, the pointers low and high will be 0 and n-1(where n = size of the given
array) respectively.
• Now inside a loop, we will perform the 3 steps discussed above in the
algorithm section.
• The loop will run until either we fount the target or any of the pointers
crosses the other.
Code :
Output : The target is at index: 2
Time Complexity :
In the algorithm, in every step, we are basically dividing the search space into 2
equal halves. This is actually equivalent to dividing the size of the array by 2, every
time. After a certain number of divisions, the size will reduce to such an extent
that we will not be able to divide that anymore and the process will stop. The
number of total divisions will be equal to the time complexity.
So the overall time complexity is O(logN), where N = size of the given array.
Approach – 02 :
Assume, the recursive function will look like this: binarySearch(nums, low, high).
It basically takes 3 parameters i.e. the array, the low pointer, and the high pointer.
In each recursive call, we will change the value of low and high pointers to trim
down the search space. Except for this, the rest of the steps will be the same.
• Step 2 : Compare the middle element with the target and trim down the
search space:
In this step, we can observe 3 different cases :
a. If arr[mid] == target : We have found the target. From this step, we can
return the index of the target, and the recursion will end.
b. If target > arr[mid] : This case signifies our target is located on the
right half of the array. So, the next recursion call will be
binarySearch(nums, mid+1, high).
c. If target < arr[mid] : This case signifies our target is located on the left
half of the array. So, the next recursion call will be binarySearch(nums,
low, mid-1).
• Base case : Base case of the recursion will be low > high. If (low > high), the
search becomes invalid which means the target is not present in the array.
Output : The target is at index: 2
Time Complexity :
In the algorithm, in every step, we are basically dividing the search space into 2
equal halves. This is actually equivalent to dividing the size of the array by 2, every
time. After a certain number of divisions, the size will reduce to such an extent
that we will not be able to divide that anymore and the process will stop. The
number of total divisions will be equal to the time complexity.
So the overall time complexity is O(logN), where N = size of the given array.
SEARCH IN ROTATED SORTED ARRAY
( LEETCODE - 33 )
03 March 2024 20:09
Given the array nums after the possible rotation and an integer target,
return the index of target if it is in nums, or -1 if it is not in nums.
Example 1 :
Example 2 :
Example 3 :
Constraints :
Link : https://leetcode.com/problems/search-in-rotated-sorted-
array/description/
Solution :
Let’s consider a sorted array: {1, 2, 3, 4, 5}. If we rotate this array at index 3, it
will become: {4, 5, 1, 2, 3}. In essence, we moved the element at the last index to
the front, while shifting the remaining elements to the right. We performed this
process twice.
Solution – 01 :
One straightforward approach we can consider is using the linear search algorithm.
Using this method, we will traverse the array to find the location of the target
value. If it is found we will simply return the index and otherwise, we will return
-1.
Algorithm :
We will traverse the array and check every element if it is equal to k. If we find
any element, we will return its index.
Otherwise, we will return -1.
Output : The index is: 3
Complexity Analysis :
Solution – 02 :
Here, we can easily observe, that we have to search in a sorted array. That is why,
we can think of using the Binary Search algorithm to solve this problem.
Observation :
To utilize the binary search algorithm effectively, it is crucial to ensure that the
input array is sorted. By having a sorted array, we guarantee that each index
divides the array into two sorted halves. In the search process, we compare the
target value with the middle element, i.e. arr[mid], and then eliminate either the
left or right half accordingly. This elimination becomes feasible due to the
inherent property of the sorted halves(i.e. Both halves always remain sorted).
However, in this case, the array is both rotated and sorted. As a result, the
property of having sorted halves no longer holds. This disruption in the sorting
order affects the elimination process, making it unreliable to determine the
target’s location by solely comparing it with arr[mid]. To illustrate this situation,
consider the following example :
Key Observation :
Though the array is rotated, we can clearly notice that for every index, one of the
2 halves will always be sorted. In the above example, the right half of the index
mid is sorted.
So, to efficiently search for a target value using this observation, we will follow a
simple two-step process.
Algorithm :
1. Place the 2 pointers i.e. low and high: Initially, we will place the pointers like
this: low will point to the first index, and high will point to the last index.
2. Calculate the ‘mid’: Now, inside a loop, we will calculate the value of ‘mid’ using
the following formula:
mid = (low+high) / 2 ( ‘/’ refers to integer division)
3. Check if arr[mid] == target: If it is, return the index mid.
4. Identify the sorted half, check where the target is located, and then
eliminate one half accordingly :
a. If arr[low] <= arr[mid]: This condition ensures that the left part is
sorted.
i. If arr[low] <= target && target <= arr[mid]: It signifies that the
target is in this sorted half. So, we will eliminate the right half (high
= mid-1).
ii. Otherwise, the target does not exist in the sorted half. So, we will
eliminate this left half by doing low = mid+1.
b. Otherwise, if the right half is sorted :
i. If arr[mid] <= target && target <= arr[high]: It signifies that the
target is in this sorted right half. So, we will eliminate the left half
(low = mid+1).
ii. Otherwise, the target does not exist in this sorted half. So, we will
eliminate this right half by doing high = mid-1.
5. Once, the ‘mid’ points to the target, the index will be returned.
6. This process will be inside a loop and the loop will continue until low crosses
high. If no index is found, we will return -1.
Code :
Output : The index is: 3
Complexity Analysis :