Facebook Pixel

278. First Bad Version

Problem Description

You're managing a product with multiple versions numbered from 1 to n. Each new version is built on top of the previous one. Unfortunately, one of the versions has a bug, and since each version depends on the previous one, all versions after the bad version are also bad.

Your task is to find the first bad version that caused all subsequent versions to fail.

You have access to an API function isBadVersion(version) that returns:

  • true if the given version is bad
  • false if the given version is good

The goal is to identify the first bad version while minimizing the number of API calls.

For example, if you have 5 versions [1, 2, 3, 4, 5] and version 3 is the first bad one, then:

  • isBadVersion(1) returns false
  • isBadVersion(2) returns false
  • isBadVersion(3) returns true
  • isBadVersion(4) returns true
  • isBadVersion(5) returns true

You need to implement a function firstBadVersion(n) that returns the version number of the first bad version.

The key challenge is to find this version efficiently without checking every single version sequentially, as the number of versions n could be very large.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

Since we have versions arranged in order from 1 to n, and we know that once a version is bad, all subsequent versions are also bad, this creates a clear pattern: there's a point where the versions transition from good to bad.

The versions look something like this: [false, false, false, true, true, true] where true means bad. We need to find where this transition happens - specifically, the first true position.

This is exactly the pattern our binary search template is designed for! The feasible function is simply isBadVersion(mid), which returns:

  • false for good versions
  • true for bad versions

The pattern [false, false, ..., true, true, ...] is monotonic, and we want to find the first true (first bad version).

Using binary search:

  • If isBadVersion(mid) returns true, we've found a bad version. Record it and search left for an earlier bad version.
  • If isBadVersion(mid) returns false, the first bad version must be to the right.

This allows us to find the first bad version in O(log n) API calls instead of O(n) by eliminating half the search space with each check.

Solution Implementation

1# The isBadVersion API is already defined for you.
2# def isBadVersion(version: int) -> bool:
3
4
5class Solution:
6    def firstBadVersion(self, n: int) -> int:
7        """
8        Find the first bad version using the binary search template.
9        Feasible condition: isBadVersion(mid) returns true
10        """
11        # Binary search template
12        left, right = 1, n
13        first_true_index = -1
14
15        while left <= right:
16            mid = (left + right) // 2
17
18            # Feasible: is this version bad?
19            if isBadVersion(mid):
20                first_true_index = mid
21                right = mid - 1  # Search for earlier bad version
22            else:
23                left = mid + 1
24
25        return first_true_index
26
1/* The isBadVersion API is defined in the parent class VersionControl.
2      boolean isBadVersion(int version); */
3
4public class Solution extends VersionControl {
5    /**
6     * Finds the first bad version using the binary search template.
7     * Feasible condition: isBadVersion(mid) returns true
8     */
9    public int firstBadVersion(int n) {
10        // Binary search template
11        int left = 1;
12        int right = n;
13        int firstTrueIndex = -1;
14
15        while (left <= right) {
16            int mid = left + (right - left) / 2;
17
18            // Feasible: is this version bad?
19            if (isBadVersion(mid)) {
20                firstTrueIndex = mid;
21                right = mid - 1;  // Search for earlier bad version
22            } else {
23                left = mid + 1;
24            }
25        }
26
27        return firstTrueIndex;
28    }
29}
30
1// The API isBadVersion is defined for you.
2// bool isBadVersion(int version);
3
4class Solution {
5public:
6    int firstBadVersion(int n) {
7        // Binary search template
8        int left = 1;
9        int right = n;
10        int firstTrueIndex = -1;
11
12        while (left <= right) {
13            int mid = left + (right - left) / 2;
14
15            // Feasible: is this version bad?
16            if (isBadVersion(mid)) {
17                firstTrueIndex = mid;
18                right = mid - 1;  // Search for earlier bad version
19            } else {
20                left = mid + 1;
21            }
22        }
23
24        return firstTrueIndex;
25    }
26};
27
1/**
2 * The isBadVersion API is defined in the parent class Relation.
3 * isBadVersion(version: number): boolean {
4 *     ...
5 * };
6 */
7
8/**
9 * Finds the first bad version using the binary search template.
10 * Feasible condition: isBadVersion(mid) returns true
11 */
12const solution = function (isBadVersion: (version: number) => boolean): (n: number) => number {
13    return function (n: number): number {
14        // Binary search template
15        let left: number = 1;
16        let right: number = n;
17        let firstTrueIndex: number = -1;
18
19        while (left <= right) {
20            const mid: number = Math.floor((left + right) / 2);
21
22            // Feasible: is this version bad?
23            if (isBadVersion(mid)) {
24                firstTrueIndex = mid;
25                right = mid - 1;  // Search for earlier bad version
26            } else {
27                left = mid + 1;
28            }
29        }
30
31        return firstTrueIndex;
32    };
33};
34

Solution Approach

We use the standard binary search template to find the first bad version. The feasible function is isBadVersion(mid) which directly gives us the false, false, ..., true, true pattern.

Template Implementation:

left, right = 1, n
first_true_index = -1

while left <= right:
    mid = (left + right) // 2
    if isBadVersion(mid):  # feasible condition
        first_true_index = mid
        right = mid - 1  # search for earlier bad version
    else:
        left = mid + 1

return first_true_index

How it works:

  1. Initialize left = 1, right = n, and first_true_index = -1
  2. Use while left <= right loop (NOT left < right)
  3. Check isBadVersion(mid) as the feasible condition
  4. If true (bad version found), record first_true_index = mid and search left with right = mid - 1
  5. If false (good version), search right with left = mid + 1
  6. Return first_true_index as the first bad version

The key difference from other template variants:

  • We use first_true_index to track the answer (NOT just returning left at the end)
  • We use while left <= right (NOT while left < right)
  • We use right = mid - 1 when feasible (NOT right = mid)

This approach guarantees we'll find the first bad version in O(log n) time complexity.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through finding the first bad version when n = 8 and version 5 is the first bad one, using the binary search template.

Initial setup: versions [1, 2, 3, 4, 5, 6, 7, 8] where versions 1-4 are good (false) and 5-8 are bad (true).

Initial State:

  • left = 1, right = 8
  • first_true_index = -1

Iteration 1:

  • left = 1, right = 8
  • mid = (1 + 8) // 2 = 4
  • isBadVersion(4)false (version 4 is good)
  • Update: left = mid + 1 = 5

Iteration 2:

  • left = 5, right = 8
  • mid = (5 + 8) // 2 = 6
  • isBadVersion(6)true (version 6 is bad)
  • Update: first_true_index = 6, right = mid - 1 = 5

Iteration 3:

  • left = 5, right = 5
  • mid = (5 + 5) // 2 = 5
  • isBadVersion(5)true (version 5 is bad)
  • Update: first_true_index = 5, right = mid - 1 = 4

Termination:

  • left = 5 > right = 4, exit loop
  • Return first_true_index = 5

We found the first bad version (5) with only 3 API calls. The key observation is that we tracked first_true_index to remember the earliest bad version found, while continuing to search left for potentially earlier bad versions.

Time and Space Complexity

The time complexity is O(log n), where n is the input parameter representing the total number of versions. This is because the algorithm uses binary search, which repeatedly divides the search space in half. In each iteration, the algorithm eliminates half of the remaining search space by checking the middle element with isBadVersion(mid). The number of iterations required is logarithmic with respect to n, specifically ⌈log₂(n)⌉ iterations in the worst case.

The space complexity is O(1), as the algorithm only uses a constant amount of extra space. The variables l, r, and mid are the only additional memory allocations, and they don't depend on the size of the input n. The algorithm operates in-place without using any recursive calls or additional data structures that would scale with the input size.

Common Pitfalls

1. Using Wrong Binary Search Template Variant

Pitfall: Using while left < right with right = mid instead of the standard template with while left <= right and right = mid - 1.

# WRONG - different template variant
while left < right:
    mid = (left + right) // 2
    if isBadVersion(mid):
        right = mid
    else:
        left = mid + 1
return left

Solution: Use the standard binary search template with first_true_index:

# CORRECT - standard template
left, right = 1, n
first_true_index = -1

while left <= right:
    mid = (left + right) // 2
    if isBadVersion(mid):
        first_true_index = mid
        right = mid - 1
    else:
        left = mid + 1

return first_true_index

2. Integer Overflow in Middle Calculation

Pitfall: When calculating the middle point using mid = (left + right) // 2, if both left and right are very large integers, their sum could overflow in some languages.

Solution: Use the overflow-safe formula:

mid = left + (right - left) // 2

3. Starting Search from Wrong Index

Pitfall: Starting with left = 0 instead of left = 1, forgetting that versions are numbered from 1 to n.

Solution: Always initialize with left = 1, right = n to match the problem's version numbering.

4. Returning left Instead of first_true_index

Pitfall: Using the template but then returning left at the end instead of first_true_index, which can give incorrect results in edge cases.

Solution: Always return first_true_index which tracks the actual first bad version found during the search.

Loading...
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Consider the classic dynamic programming of fibonacci numbers, what is the recurrence relation?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More