278. First Bad Version
On LeetCode ->Problem¶
Find the smallest version in 1..n such that isBadVersion(v) is True, knowing:
- all versions before it are good
- it and all later versions are bad
- you should use as few API calls as possible
Example:
n=5, state=[good, good, good, bad, bad]->4
Key trick¶
Use binary search on the boundary between:
- good prefix
- bad suffix
Keep the first bad candidate and move left when mid is bad.
Trap¶
Common mistakes:
- using linear search, which wastes API calls
- off-by-one errors with
left,right, and loop condition - forgetting to return the left boundary directly
- initializing an answer variable unnecessarily
- not handling
n=1
Why is it interesting?¶
It is a clean "find first true" binary search.
It tests whether you really understand boundary-search, not just ordinary value lookup.
Python solution¶
class Solution:
def firstBadVersion(self, n: int) -> int:
l, r = 1, n
while l <= r:
mid = (l + r) // 2
if isBadVersion(mid):
r = mid - 1
else:
l = mid + 1
return l
Comment on my solution¶
Good:
- correct binary search idea
- updates bounds correctly
- works on the provided cases
Can be improved:
first_bad_vis not neededwhile (max_v - min_v) >= 0:is harder to read thanwhile left < right- names like
min_vandmax_vare less standard thanleftandright
def isBadVersion(version):
pass
class Solution:
def firstBadVersion(self, n: int) -> int:
first_bad_v = 1
min_v = 1
max_v = n
while (max_v - min_v) >= 0:
v = min_v + (max_v - min_v) // 2
if isBadVersion(v):
first_bad_v = v
max_v = v - 1
else:
min_v = v + 1
return first_bad_v
import pytest
import leetcode_top_easy_05_sorting_and_searching_02_first_bad_version as mod
@pytest.mark.parametrize(
("version_fails", "first_bad_v"),
[
([True], 1),
([False, False, True, True, True], 3),
([False, True, True, True, True], 2),
([False, True, True, True], 2),
([False, False, True], 3),
]
)
def test_firstBadVersion(version_fails, first_bad_v, monkeypatch):
def fake_isBadVersion(version):
return version_fails[version - 1]
monkeypatch.setattr(mod, "isBadVersion", fake_isBadVersion)
assert Solution().firstBadVersion(len(version_fails)) == first_bad_v
A simpler version is: