First Bad Version

Given the last version number, find the first corrupted version.

Example

I: [T,T,T,F,F,F,F,F], 8
O: 4

What if there are no bad versions? Return 0

Brute Force

Linear scan all the versions and find the first bad commit

def firstBadVersion(self, n: int) -> int:
    for i in range(1, n+1):
        if isBadVersion(i):
            return i
public class Solution extends VersionControl {
    public int firstBadVersion(int lastVersion) {
        int v;
        for (v = 1; v <= lastVersion; v++) {
            if (isBadVersion(v)) {1
                return v;
            }
        }
        return 0;
    }
}

Time limit exceeded with input size

Since the search space well-ordered, we can decompose it with an O(1) check.

Divide and Conquer

This is more like just dividing.

How to select middle index

If we use this simple formula s+e2\frac{s + e}{2} , then left section should be [s,m][s, m]and right section should be (m,e](m, e]for even distribution in these 2 cases. We can get smart to exclude the middle element if it is not the target but that is a minor optimization.

Recursion solution is easier to understand and write. But I suspect the performance is worse (at least for space efficiency).

The two pointer solution above is harder to write but is a little more performant.

Recursive solution

Two pointer solution

Runtime: 32 ms

Memory Usage: 14.4 MB

Runtime: 32 ms

Memory Usage: 14.2 MB

Turns out the performance difference is minimal. I don't think a logn\log{n}algorithm can cause stack overflow.

Note: / does true division. // does floor division.

Last updated