➜ ~

Playing Hacks and Stuffs!


Project maintained by h4ckyou Hosted on GitHub Pages — Theme by mattgraham

Binary Search Algorithm

Binary Search is defined as a searching algorithm used in a sorted array by repeatedly dividing the search interval in half. The idea of binary search is to use the information that the array is sorted and reduce the time complexity to O(log N). image

Conditions for when to apply Binary Search in a Data Structure:

To apply Binary Search algorithm:

Binary Search Algorithm:

In this algorithm,

How to Implement Binary Search?

The Binary Search Algorithm can be implemented in the following two ways

Complexity Analysis of Binary Search:

Time Complexity:

Auxiliary Space: O(1), If the recursive call stack is considered then the auxiliary space will be O(logN).

Advantages of Binary Search:

Drawbacks of Binary Search:

Applications of Binary Search:

SOURCE: GeeksForGeeks

Example 1:

image

Given an array of integers nums which is sorted in ascending order, and an integer target, write a function to search target in nums. If target exists, then return its index. Otherwise, return -1.

Let us look at the first example case they gave us:

Input: nums = [-1,0,3,5,9,12], target = 9
Output: 4
Explanation: 9 exists in nums and its index is 4

So we are to find the index position of a target value in an array

The best algorithm to use here of cause is Binary Search because the list is sorted

First thing we need is this:

L = 0
R = len(nums) - 1
mid = L + (R - L) // 2
target = 9

Now we will start from the center of the array:

[-1,0,3,5,9,12]

The center index of the array is 3

image

>>> nums = [-1,0,3,5,9,12]
>>> L = 0
>>> R = len(nums) - 1
>>> mid = L + (R - L) // 2
>>> target = 9
>>> 
>>> nums[mid]
3
>>>

Now we check the condition:

That’s the logic of Iterative Binary Search Algorithm since we will be doing this in a loop and not recursively

Doing that manually wouldn’t take time since this array is small in terms of the length

But it’s best to put your coding skill in practice

So I wrote a script to solve that!

Here’s the script:

# Iterative Binary Search Algorithm

def binarySearch(arr, x):
    l = 0
    r = len(arr)-1

    while l <= r:
        
        mid = l + (r - l) // 2

        if arr[mid] == x:
            return mid

        elif arr[mid] < x:
            l = mid + 1

        else:
            r = mid - 1
        
    return -1


arr = [-1,0,3,5,9,12]
x = 9

result = binarySearch(arr, x)
print(result)

Using it I got the value of the target which is 4 image

We can run the script and use the other case sample image image

It returned -1 because the target value we are searching for isn’t among the array

Here’s a sample script using Recursive Binary Search

# Recursive Binary Search Algorithm

def binarySearch(arr, x, l, r):
    mid = l + (r-l)//2

    while r >= 0:

        if arr[mid] == x:
            return mid
        
        elif arr[mid] < x:
            return binarySearch(arr, x, l+1, r)
        
        else:
            return binarySearch(arr, x, l, mid-1)


arr = [-1,0,3,5,9,12]
x = 9
l = 0
r = len(arr)-1

result = binarySearch(arr, x, l, r)
print(result)

Now that we have a working solution we can now use the submission template and write the code there image

Wait what Time Limit Exceeded!!

Is python this slow for their time complexity?

But it takes very less seconds when I run it on my laptop image

Well since it’s their custom Class they maybe will add some time complexity which my program didn’t succeed in passing it

Now what?

Well I moved to C++ since it’s more faster

Here’s the code

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int n = nums.size();
        int r = n - 1;
        int l = 0;
        
        while (l <= r) {
            int m = l + (r - l) / 2; 
            
            if (nums[m] == target)
                return m;
            
            if (nums[m] < target)
                l = m + 1;
            
            else
                r = m - 1;
        }
        
        return -1; 
    }
};

Running it I passed the custom testcase with a program runtime of 0ms image

Now I can submit the code and boom it worked! image

I was wondering why python didn’t work so I checked the script and found a bug :( image

The middle variable is supposed to be inside the while loop image

So after making that edit it worked image

But still C++ is pretty much faster 🙂

Example 2

image

Given a non-negative integer x, return the square root of x rounded down to the nearest integer. The returned integer should be non-negative as well.

You must not use any built-in exponent function or operator.

For example, do not use pow(x, 0.5) in c++ or x ** 0.5 in python.

Ok so the question is pretty understandable!

We are going to be given a non-negative integer x and we are to provide it’s square root without using any builtins function like pow()

This seemed intimidating to me at first when I tried it but after few minutes of thinking I got a breakthrough

First let’s take a case sample from the challenge

Input: x = 4
Output: 2
Explanation: The square root of 4 is 2, so we return 2.

Given the number 4 what’s the square root?

Here’s my approach:

Of cause we should know that the square root of a number is a factor of a number that, when multiplied by itself, gives the original number

With that we just need to find an integer whereby if we take the square of it i.e multiply it with itself we will get the number 4

We can do that with like brute force approach i.e

1 * 1 == 4 False
2 * 2 == 4 True

That works! But let’s say the number is large?

Well then this method will take some significant amount of time

And we should know that when we submit a working solution it will also check it based on time complexity i.e there’s a time limit expected for a program to run

So it’s best we make our program more optimized and time efficient

Now how do we achieve that?

This is what I thought 🤔

If we can create an array in the range of the target number we can then use binary search algorithm to get the number we are looking for

Let’s take two logical examples of what I’ll do:

Input: x = 16
Output: 4
Explanation: The square root of 16 is 4, so we return 4.

First we create an array in the range of the input value:

➜  Learn python3
Python 3.11.2 (main, Feb 12 2023, 00:48:52) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> num = 16
>>> array = [i for i in range(1, num)]
>>> 
>>> array
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
>>>

Notice that I started my range from 1 to num and not 0 to num

Having 0 is not really an issue but for case where 0 will be the answer is when the input is 0

Now that we have the array and since it’s sorted we can apply binary search algorithm here

Basically we’ll check if the array[middle] * array[middle] == target

➜  Learn python3
Python 3.11.2 (main, Feb 12 2023, 00:48:52) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> num = 16
>>> array = [i for i in range(1, num)]
>>> 
>>> array
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
>>> 
>>> left = 0
>>> right = len(array)-1
>>> middle = left + (right - left) // 2
>>> 
>>> square = array[middle]*array[middle]
>>> 
>>> square
64
>>> 
>>> square == num
False
>>> 

Well we can see that the comparison returned False and that’s true because the statement is False

Next we can shift the search space to the left since the value compared with is greater than the input value

>>> right = middle - 1
>>> middle = left + (right - left) // 2
>>> square = array[middle]*array[middle]
>>>                                                                                                                                                                                          
>>> square
16                                                                                                                                                                                           
>>>                                                                                                                                                                                          
>>> square == num                                                                                                                                                                            
True                                                                                                                                                                                         
>>>

Now that returns True because 16 is indeed equal to 16

So the square root of the number 16 will be the array[middle] and that’s 4

>>> array[middle]                                                                                                                                                                            
4                                                                                                                                                                                            
>>>

With that we’ve optimized the search

How about case two?

Input: x = 8
Output: 2
Explanation: The square root of 8 is 2.82842..., and since we round it down to the nearest integer, 2 is returned.

With just looking at the input number we can tell that it isn’t a perfect square

Perfect squares are numbers whose square roots are whole numbers

In this case we are asked to return the approximated value of the nearest integer

So if we are suppose to have a float number i.e 4.123105625617661 the approximate value will be 4

With that said let’s get to it

First I’ll get the array of numbers within the input range

>>> num = 8
>>> array = [i for i in range(1, num)]
>>> 
>>> array
[1, 2, 3, 4, 5, 6, 7]
>>>

Then I’ll store the required parameters and start the logic

>>> left = 0
>>> right = len(array) - 1
>>> middle = left + (right - left) // 2
>>> 
>>> square = array[middle]*array[middle]
>>> 
>>> square == num
False
>>> 
>>> square
16
>>>

So we see that the square is greater than input value

We can shift our search space to the right

>>> right = middle - 1
>>> middle = left + (right - left) // 2
>>> square = array[middle]*array[middle]
>>> 
>>> square
4
>>> 
>>> square == num
False
>>>

Ok now the comparison returned False and that’s True since the value of square is 4 and the input number is 8

In this case this condition of the program comes in:

if square < num:
  left = middle + 1

So there isn’t really any more range again for this because if we move the search space to the right then the condition which shifts the search space to the left triggers

Therefore since no more range for this the answer returned would be array[middle] which is 2

>>> array[middle]
2
>>>

With that said we now understand how to implement this algorithm in solving this task

Here’s my solve script

def sqrt(num):
    array = [i for i in range(1, num)]
    left = 0
    right = len(array) - 1
    ans = -1
    
    if num == 0:
        return -1

    while left <= right:
        middle = left + (right - left) // 2
        square = array[middle] * array[middle]

        if square == num:
            ans = array[middle]
            break

        elif square > num:
            right = middle - 1

        else:
            left = middle + 1
            ans = array[middle]  

    return ans

num = 16
result = sqrt(num)
print(result)

Brief explanation of the conditional checks happening there:

We can check for it’s authenticity by running it with various testcases image image

Now imagine using large number? image image

The time it took was just 0.14s if you compare this with the brute force approach you will notice significant difference in time

Now that we’re done let’s paste it in the template given to this

I submitted it and got a wrong answer image

I then remember I didn’t check constraint

image

So the input number can be within range 0

Editing my script to use the right range, removing the if check comparing the number with 0 & changin the ans variable from -1 to 0

def sqrt(num):
    array = [i for i in range(num+1)]
    left = 0
    right = len(array) - 1
    ans = 0

    while left <= right:
        middle = left + (right - left) // 2
        square = array[middle] * array[middle]

        if square == num:
            ans = array[middle]
            break

        elif square > num:
            right = middle - 1

        else:
            left = middle + 1
            ans = array[middle]  

    return ans

num = int(input())
result = sqrt(num)
print(result)

Then on running it again I got this error image

The program actually uses a lot of memory due the fact I’m saving the range in a loop and if a very large number is given it would take lot of memory maybe even crashing your laptop if you dont have high ram :(

So I decided to figure a way to optimize the code

After research I figured that the use of creating that array is not memory efficient as we can directly work on the number even without it being in an array (cool right?)

Modification to the script lead me to this:

def sqrt(num):
    left, right = 0, num
    ans = 0

    while left <= right:
        middle = left + (right - left) // 2
        square = middle * middle

        if square == num:
            ans = middle
            break

        elif square > num:
            right = middle - 1

        else:
            left = middle + 1
            ans = middle  

    return ans

num = int(input())
result = sqrt(num)
print(result)

I used it on the template and it worked ^^ image

Compared to when I used the array methd :( and this which directly works on the number, using a large number didn’t crash my laptop but instead gave me it’s square root in 0.09s image

Example 3

image image

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 will tell you whether the number I picked is higher or lower than your guess.

You call a pre-defined API int guess(int num), which returns three possible results:

-1: Your guess is higher than the number I picked (i.e. num > pick).
1: Your guess is lower than the number I picked (i.e. num < pick).
0: your guess is equal to the number I picked (i.e. num == pick).

Return the number that I picked.

So basically we are going to be given n whose range is between 1 <= n <= 231 - 1

Our goal is to call the guess function which is already predefined in the code

The function requires an argument to be passed into it which is the number to be guessed

The goal of this task is to guess the right number

We can easily just brute force it depending on the value of n since the number to be guessed is in the range of n i.e 1 <= pick <= n

But I won’t do that because of it will take time and our code needs to be as fast as possible

Looking at the description they also gave us a good return result value needed to solve this

-1: Your guess is higher than the number I picked (i.e. num > pick).
1: Your guess is lower than the number I picked (i.e. num < pick).
0: your guess is equal to the number I picked (i.e. num == pick).

From that we can basically determine if our number is greater than the picked number or less or equal

This gives us a good chance to use Binary Search Algorithm

Just the standard logic

Here’s my solve script which is in it’s template form

class Solution(object):
    def guessNumber(self, n):
        """
        :type n: int
        :rtype: int
        """
        lower, higher = 1, n
        
        while lower <= higher:
            middle = lower + (higher - lower) // 2
            result = guess(middle)
            
            if result == 0:
                return middle
        
            elif result == -1:
                higher = middle - 1
            
            else:
                lower = middle + 1

Submitting it works image

Example4

Description: Alice has some cards with numbers written on them. She arranges the cards in decreasing order, and lays them out face down in a sequence on a table. She challenges Bob to pick out the card containing a given number by turning over as few cards as possible. Can you help Bob locate the card:

cards = [8, 8, 6, 6, 6, 6, 6, 6, 3, 2, 2, 2, 0, 0, 0]
query = 6

The key note is that the card list is arranged in decreasing sequence which makes Binary Search Algorithm a gateway to solve this

Well since the query of the expected card is 6 that’s an issue

Cause looking at the numbers in the card we can see there are quite retition of 6s

And if we apply Binary Search it will return the middle number which in this case is 6

So that’s a false result because there are other 6’s before it making it not the first occurrence

To solve this I’ll basically do this:

Then using the normal standard Binary Search Algorithm I’ll get the right value

Here’s my solve script

def getRightIndex(cards, query, mid):
    mid_number = cards[mid]

    if mid_number == query:
        if mid-1 >= 0 and cards[mid-1] == query:
            return "left"
        else:
            return "found"

    elif mid_number < query:
        return "left"

    else:
        return "right"

def binarySeach(cards, query):
    left, right = 0, len(cards) -1 

    while left <= right:
        mid = left + (right - left) // 2
        r = getRightIndex(cards, query, mid)

        if r == "found":
            return mid

        elif r == "left":
            right = mid + 1
        
        else:
            left = mid - 1
    
    return -1


cards = [8, 8, 6, 6, 6, 6, 6, 6, 3, 2, 2, 2, 0, 0, 0]
query = 6

r = binarySeach(cards, query)
print(r)

Running it works image

We can confirm it is indeed right because it’s the first occurrence of 6 in it’s respective index value