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:
trueif the given version is badfalseif 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)returnsfalseisBadVersion(2)returnsfalseisBadVersion(3)returnstrueisBadVersion(4)returnstrueisBadVersion(5)returnstrue
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.
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:
falsefor good versionstruefor 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)returnstrue, we've found a bad version. Record it and search left for an earlier bad version. - If
isBadVersion(mid)returnsfalse, 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
261/* 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}
301// 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};
271/**
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};
34Solution 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:
- Initialize
left = 1,right = n, andfirst_true_index = -1 - Use
while left <= rightloop (NOTleft < right) - Check
isBadVersion(mid)as the feasible condition - If true (bad version found), record
first_true_index = midand search left withright = mid - 1 - If false (good version), search right with
left = mid + 1 - Return
first_true_indexas the first bad version
The key difference from other template variants:
- We use
first_true_indexto track the answer (NOT just returningleftat the end) - We use
while left <= right(NOTwhile left < right) - We use
right = mid - 1when feasible (NOTright = 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 EvaluatorExample 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 = 8first_true_index = -1
Iteration 1:
left = 1,right = 8mid = (1 + 8) // 2 = 4isBadVersion(4)→false(version 4 is good)- Update:
left = mid + 1 = 5
Iteration 2:
left = 5,right = 8mid = (5 + 8) // 2 = 6isBadVersion(6)→true(version 6 is bad)- Update:
first_true_index = 6,right = mid - 1 = 5
Iteration 3:
left = 5,right = 5mid = (5 + 5) // 2 = 5isBadVersion(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.
Consider the classic dynamic programming of fibonacci numbers, what is the recurrence relation?
Recommended Readings
https assets algo monster cover_photos Binary_Search svg Binary Search Intuition Binary search is an efficient array search algorithm It works by narrowing down the search range by half each time If you have looked up a word in a physical dictionary you've already used binary search in real life Let's
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion If you prefer videos here's a video that explains recursion in a fun and easy way Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask him
Want a Structured Path to Master System Design Too? Don’t Miss This!