Skip to content

Commit f0a2a39

Browse files
committed
feat: add First Bad Version
1 parent 5b27fa9 commit f0a2a39

File tree

7 files changed

+247
-1
lines changed

7 files changed

+247
-1
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
{
2+
"problem_name": "first_bad_version",
3+
"solution_class_name": "Solution",
4+
"problem_number": "278",
5+
"problem_title": "First Bad Version",
6+
"difficulty": "Easy",
7+
"topics": "Binary Search, Interactive",
8+
"tags": ["grind-75"],
9+
"readme_description": "You are a product manager and currently leading a team to develop a new product. Unfortunately, the latest version of your product fails the quality check. Since each version is developed based on the previous version, all the versions after a bad version are also bad.\n\nSuppose you have `n` versions `[1, 2, ..., n]` and you want to find out the first bad one, which causes all the following ones to be bad.\n\nYou are given an API `bool isBadVersion(version)` which returns whether `version` is bad. Implement a function to find the first bad version. You should minimize the number of calls to the API.",
10+
"readme_examples": [
11+
{
12+
"content": "```\nInput: n = 5, bad = 4\nOutput: 4\n```\n**Explanation:**\n```\ncall isBadVersion(3) -> false\ncall isBadVersion(5) -> true\ncall isBadVersion(4) -> true\n```\nThen 4 is the first bad version."
13+
},
14+
{ "content": "```\nInput: n = 1, bad = 1\nOutput: 1\n```" }
15+
],
16+
"readme_constraints": "- 1 <= bad <= n <= 2^31 - 1",
17+
"readme_additional": "**Note:** The `isBadVersion` API is already defined for you.",
18+
"solution_imports": "",
19+
"solution_methods": [
20+
{
21+
"name": "is_bad_version",
22+
"parameters": "version: int",
23+
"return_type": "bool",
24+
"dummy_return": "False"
25+
},
26+
{
27+
"name": "first_bad_version",
28+
"parameters": "n: int",
29+
"return_type": "int",
30+
"dummy_return": "1"
31+
}
32+
],
33+
"test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .solution import Solution",
34+
"test_class_name": "FirstBadVersion",
35+
"test_helper_methods": [
36+
{ "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" },
37+
{
38+
"name": "is_bad_version",
39+
"parameters": "version: int, bad: int",
40+
"body": "return version >= bad"
41+
}
42+
],
43+
"test_methods": [
44+
{
45+
"name": "test_first_bad_version",
46+
"parametrize": "n, bad, expected",
47+
"parametrize_typed": "n: int, bad: int, expected: int",
48+
"test_cases": "[(5, 4, 4), (1, 1, 1), (3, 1, 1), (10, 7, 7), (5, -1, 1)]",
49+
"body": "solution = Solution()\n# Mock is_bad_version for this test\nsolution.is_bad_version = lambda version: self.is_bad_version(version, bad)\nresult = solution.first_bad_version(n)\nassert result == expected"
50+
}
51+
],
52+
"playground_imports": "from solution import Solution",
53+
"playground_test_case": "# Example test case\nn = 5\nbad = 4\nexpected = 4",
54+
"playground_execution": "solution = Solution()\nsolution.is_bad_version = lambda version: version >= bad\nresult = solution.first_bad_version(n)\nresult",
55+
"playground_assertion": "assert result == expected"
56+
}

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
PYTHON_VERSION = 3.13
2-
PROBLEM ?= ransom_note
2+
PROBLEM ?= first_bad_version
33
FORCE ?= 0
44
COMMA := ,
55

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# First Bad Version
2+
3+
**Difficulty:** Easy
4+
**Topics:** Binary Search, Interactive
5+
**Tags:** grind-75
6+
7+
**LeetCode:** [Problem 278](https://leetcode.com/problems/first-bad-version/description/)
8+
9+
## Problem Description
10+
11+
You are a product manager and currently leading a team to develop a new product. Unfortunately, the latest version of your product fails the quality check. Since each version is developed based on the previous version, all the versions after a bad version are also bad.
12+
13+
Suppose you have `n` versions `[1, 2, ..., n]` and you want to find out the first bad one, which causes all the following ones to be bad.
14+
15+
You are given an API `bool isBadVersion(version)` which returns whether `version` is bad. Implement a function to find the first bad version. You should minimize the number of calls to the API.
16+
17+
## Examples
18+
19+
### Example 1:
20+
21+
```
22+
Input: n = 5, bad = 4
23+
Output: 4
24+
```
25+
26+
**Explanation:**
27+
28+
```
29+
call isBadVersion(3) -> false
30+
call isBadVersion(5) -> true
31+
call isBadVersion(4) -> true
32+
```
33+
34+
Then 4 is the first bad version.
35+
36+
### Example 2:
37+
38+
```
39+
Input: n = 1, bad = 1
40+
Output: 1
41+
```
42+
43+
## Constraints
44+
45+
- 1 <= bad <= n <= 2^31 - 1
46+
47+
**Note:** The `isBadVersion` API is already defined for you.

leetcode/first_bad_version/__init__.py

Whitespace-only changes.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": 1,
6+
"id": "imports",
7+
"metadata": {},
8+
"outputs": [],
9+
"source": [
10+
"from solution import Solution"
11+
]
12+
},
13+
{
14+
"cell_type": "code",
15+
"execution_count": 2,
16+
"id": "setup",
17+
"metadata": {},
18+
"outputs": [],
19+
"source": [
20+
"# Example test case\n",
21+
"n = 5\n",
22+
"bad = 4\n",
23+
"expected = 4"
24+
]
25+
},
26+
{
27+
"cell_type": "code",
28+
"execution_count": 3,
29+
"id": "execute",
30+
"metadata": {},
31+
"outputs": [
32+
{
33+
"data": {
34+
"text/plain": [
35+
"4"
36+
]
37+
},
38+
"execution_count": 3,
39+
"metadata": {},
40+
"output_type": "execute_result"
41+
}
42+
],
43+
"source": [
44+
"solution = Solution(first_bad=bad)\n",
45+
"result = solution.first_bad_version(n)\n",
46+
"result"
47+
]
48+
},
49+
{
50+
"cell_type": "code",
51+
"execution_count": 4,
52+
"id": "test",
53+
"metadata": {},
54+
"outputs": [],
55+
"source": [
56+
"assert result == expected"
57+
]
58+
}
59+
],
60+
"metadata": {
61+
"kernelspec": {
62+
"display_name": "leetcode-py-py3.13",
63+
"language": "python",
64+
"name": "python3"
65+
},
66+
"language_info": {
67+
"codemirror_mode": {
68+
"name": "ipython",
69+
"version": 3
70+
},
71+
"file_extension": ".py",
72+
"mimetype": "text/x-python",
73+
"name": "python",
74+
"nbconvert_exporter": "python3",
75+
"pygments_lexer": "ipython3",
76+
"version": "3.13.7"
77+
}
78+
},
79+
"nbformat": 4,
80+
"nbformat_minor": 5
81+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
class Solution:
2+
# TODO: template constraint
3+
def __init__(self, first_bad):
4+
self.is_bad_version = lambda version: version >= first_bad
5+
6+
# Time: O(log n)
7+
# Space: O(1)
8+
def first_bad_version(self, n: int) -> int:
9+
left = 1
10+
right = n
11+
12+
while left < right:
13+
mid = (left + right) // 2
14+
if self.is_bad_version(mid):
15+
right = mid
16+
else:
17+
left = mid + 1
18+
19+
return right
20+
21+
22+
# BISECT PATTERNS - General Binary Search
23+
# Given: arr = [10,20,30,30,30,40,50], target = 30
24+
# 0 1 2 3 4 5 6
25+
#
26+
# bisect_left: Find FIRST occurrence (leftmost insertion point)
27+
# while left < right:
28+
# if arr[mid] >= target: # >= keeps moving left
29+
# right = mid
30+
# Returns: 2 (index of first 30, value=30)
31+
# [10,20,30,30,30,40,50]
32+
# 0 1 2 3 4 5 6
33+
# ↑ index 2
34+
#
35+
# bisect_right: Find position AFTER last occurrence
36+
# while left < right:
37+
# if arr[mid] > target: # > allows equal values
38+
# right = mid
39+
# Returns: 5 (index after last 30, value=40)
40+
# [10,20,30,30,30,40,50]
41+
# 0 1 2 3 4 5 6
42+
# ↑ index 5
43+
#
44+
# Key difference: >= vs > in the condition
45+
# This problem uses bisect_left pattern to find first bad version
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import pytest
2+
3+
from leetcode_py.test_utils import logged_test
4+
5+
from .solution import Solution
6+
7+
8+
class TestFirstBadVersion:
9+
10+
@pytest.mark.parametrize(
11+
"n, bad, expected", [(5, 4, 4), (1, 1, 1), (3, 1, 1), (10, 7, 7), (5, -1, 1)]
12+
)
13+
@logged_test
14+
def test_first_bad_version(self, n: int, bad: int, expected: int):
15+
solution = Solution(first_bad=bad)
16+
result = solution.first_bad_version(n)
17+
assert result == expected

0 commit comments

Comments
 (0)