Skip to content

Commit 3dcff79

Browse files
committed
Fix binary search output for duplicates (Fixes #13840)
1 parent 678dedb commit 3dcff79

File tree

3 files changed

+169
-321
lines changed

3 files changed

+169
-321
lines changed

searches/binary_search.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,10 @@ def binary_search(sorted_collection: list[int], item: int) -> int:
207207
midpoint = left + (right - left) // 2
208208
current_item = sorted_collection[midpoint]
209209
if current_item == item:
210-
return midpoint
210+
if midpoint > 0 and sorted_collection[midpoint - 1] == item:
211+
right = midpoint - 1 # Keep searching left
212+
else:
213+
return midpoint
211214
elif item < current_item:
212215
right = midpoint - 1
213216
else:

tests/test_binary_search_issue.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import sys
2+
import os
3+
from hypothesis import given, strategies as st
4+
5+
# Add root directory to python path
6+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
7+
8+
from searches.binary_search import binary_search
9+
10+
# Strategy to generate a sorted list of integers
11+
sorted_list_strategy = st.lists(st.integers(), min_size=0).map(sorted)
12+
13+
@given(
14+
arr=sorted_list_strategy,
15+
# Pick a random integer as a potential target
16+
target=st.integers()
17+
)
18+
def test_binary_search_property_based(arr, target):
19+
"""
20+
Property-based test:
21+
1. If target is in arr, binary_search MUST return an index 'i'
22+
such that arr[i] == target.
23+
2. If target is in arr, it MUST be the FIRST occurrence.
24+
3. If target is not in arr, binary_search MUST return -1.
25+
"""
26+
27+
result = binary_search(arr, target)
28+
29+
if target in arr:
30+
assert result != -1, f"Target {target} was in {arr} but not found."
31+
assert arr[result] == target, f"Index {result} pointed to {arr[result]}, not {target}."
32+
33+
# Property: Verify it is the FIRST occurrence
34+
# Only check if the target is actually at the result index
35+
for i in range(result):
36+
assert arr[i] != target, f"Found target {target} at index {result}, but earlier occurrence at {i}."
37+
else:
38+
assert result == -1, f"Target {target} was not in {arr}, but found at index {result}."

0 commit comments

Comments
 (0)