First Bad VersionLoading saved progress…

First Bad Version

First Bad Version (LeetCode 278) asks you to find the earliest version in 1..n that is bad, given that versions are monotonic: once one version is bad, every later version is bad too. Imagine a bad commit that slips into your project — every build after it fails, and you want the exact one that introduced the break. You can only learn whether a version is bad by calling isBad(version), and each call is expensive (picture a full CI build), so the goal is to find the boundary with as few calls as possible. That "locate the boundary in a sorted range" shape is a textbook binary search.

Signature

firstBadVersion(n, isBad)
// n     — how many versions there are, labelled 1..n
// isBad — (version: number) => boolean, true when that version is bad.
//         Monotonic: if isBad(k) is true, then isBad(k + 1) ... isBad(n) are all true.
// returns the smallest version number for which isBad is true

Examples

firstBadVersion(5, (v) => v >= 4); // 4  — versions 1-3 good, 4-5 bad
firstBadVersion(5, (v) => v >= 1); // 1  — every version is bad
firstBadVersion(5, (v) => v >= 5); // 5  — only the last version is bad

Notes

  • Monotonic — the versions read good, good, ..., bad, bad: once isBad returns true it never returns false again. That ordering is exactly what makes binary search valid.
  • At least one bad — you may assume some version in 1..n is bad, so an answer always exists; you never return "not found".
  • Few checks — aim for O(log n) calls to isBad, not O(n). Each call models an expensive check, so the number of calls is the real cost.
  • isBad is the only oracle — you cannot inspect a version directly. Calling isBad(version) is the one and only way to learn whether it is good or bad.

FAQ

Why is this a binary search problem?
The versions are monotonic — good, good, then bad, bad — so one check in the middle tells you which half the first bad version is in. That lets you throw away half the range each step and find the boundary in about log2(n) checks instead of scanning all n.
Why move hi to mid instead of mid minus 1?
When isBad(mid) is true, mid might itself be the first bad version, so it must stay in the search window. Setting hi = mid - 1 could step over the answer and return a version that is one too high. Only the good branch (lo = mid + 1) may exclude mid.
Why compute the midpoint as lo + (hi - lo) / 2?
It is mathematically equal to (lo + hi) / 2 but avoids overflowing the intermediate sum when lo and hi are both very large. Since hi - lo is at most n, the subtraction form stays in range even in languages with fixed-width integers.
Loading editor…