Home First Bad Version
Post
Cancel

First Bad Version

Links

Go to my solution
Go to the question on LeetCode

My Thoughts

What Went Well
I quickly solved the question. Thanks to Thursday’s question, I identified the optimal solution while implementing my initial approach.

What I Learned
I, once again, refreshed my memory about the binary search algorithm.

How I Can Improve
The approach I took was the best one I could have. Although I knew my first implementation could have been more optimal and, most definitely, easier to understand. This approach enabled me to ease into the task and contemplate the optimal solution in the back of my mind while I worked on my initial answer.

Comments
It is essential to be able to solve a problem over finding the most optimal solution. Always get something on the page and work your way to an answer, even if it is not the most efficient. If you cannot develop an optimal solution within a reasonable amount of time, proceed with a solution you confidently implement.

Algorithm Description

Binary Search Algorithm - A search algorithm that repeatedly halves the search interval until the target variable is found at the middle index.

My implementation involves continuously halving the search interval until the left and right indices converge to the same index. This is because multiple bad solutions may make it impossible to guarantee that the middle index is the first bad solution. Hence, the algorithm must reduce the search interval to a single element to ensure a correct solution.

Visual Examples
Binary search being performed on an array that contains the target, click to view

Binary search being performed on an array that does not contain the target, click to view

Solution Statistics For The Optimal Solution

Time Spent Optimizing
10 minutes

Time Complexity
O(n log n) - Fixed-time complexity due to the question. The algorithm halves each iteration’s search interval, resulting in the O(n log n) time complexity. (Fastest possible time complexity for all search algorithms)

Space Complexity
O(1) - Only three new variables are created. The number of variables declared does not depend on the number of items in the list, resulting in the O(1) space complexity.

Runtime Beats
89.60% of other submissions

Memory Beats
48.14% of other sumbissions

Optimal Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# The isBadVersion API is already defined for you.
# def isBadVersion(version: int) -> bool:

class Solution:
    def firstBadVersion(self, n: int) -> int:
        # Left index
        l = 0               
        # Right index
        r = n - 1

        while r >= l:       
            # Middle index = sum both indices, divide by 2, and floor the result
            m = (l+r)//2

            if isBadVersion(m):
                # -1 to avoid checking the same value twice and infinite looping
                r = m - 1   
            
            else:
                # +1 to avoid checking the same value twice and infinite looping
                l = m + 1  

        # Since the loop exits after the right index is greater than or equal to 'l'
        # 'l' will always hold the first bad version
        return l

Solution Statistics For The Original Solution

Time Spent Coding
10 minutes

Time Complexity
O(n * a) - As n approaches infinity, the fact that we increment by 10,000 does not matter. Also, the time it takes to interact with the API (a) affects our time complexity and is a bottle neck for our program’s run time because we can safely assume it takes more time than incrementing i. In the end, each increment of n requires a call to a, resulting in the O(n * a).

Space Complexity
O(1) - Only one new variable is created. The number of variables declared does not depend on the number of items in the list, resulting in the O(1) space complexity.

Runtime Beats
5.90% of other submissions

Memory Beats
48.14% of other sumbissions

Original Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution:
    def firstBadVersion(self, n: int) -> int:
        # Edge case
        if n == 1:
            return int(isBadVersion(1))

        # Iterate until n at steps of 10,000
        # Max n is 2^31, so without an increased step, the program would timeout
        for i in range(0,n,10000):
            # If isBadVersion(i) == True:
            if isBadVersion(i):         
                break

        # i is saved from the previous for loop
        # Iterate from the previous interval (i-10,000) till n.
        # First bad will be found within 10,001 increments of i
        # If we have a small n, subtracting 10000 may result in a negative number, so use the largest starting index max(0,i-10000)
        for i in range(max(0,i-10000),n):
            if isBadVersion(i):
                return i

        return n
This post is licensed under CC BY 4.0 by the author.

Balanced Binary Tree

Maximum Subarray