Skip to content

Commit 1d46ea5

Browse files
CommitTodaySoulSniper1212pre-commit-ci[bot]
authored
Fix binary search to return leftmost occurrence for duplicates (#13891)
* Fix binary search to return leftmost occurrence for duplicates Signed-off-by: SoulSniper1212 <warush23@gmail.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: SoulSniper1212 <warush23@gmail.com> Co-authored-by: SoulSniper1212 <warush23@gmail.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 00d9ebb commit 1d46ea5

1 file changed

Lines changed: 38 additions & 13 deletions

File tree

searches/binary_search.py

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,9 @@ def binary_search(sorted_collection: list[int], item: int) -> int:
185185
186186
:param sorted_collection: some ascending sorted collection with comparable items
187187
:param item: item value to search
188-
:return: index of the found item or -1 if the item is not found
188+
:return: index of the found item or -1 if the item is not found.
189+
If there are multiple occurrences of the item, returns the index
190+
of the leftmost occurrence.
189191
190192
Examples:
191193
>>> binary_search([0, 5, 7, 10, 15], 0)
@@ -196,22 +198,30 @@ def binary_search(sorted_collection: list[int], item: int) -> int:
196198
1
197199
>>> binary_search([0, 5, 7, 10, 15], 6)
198200
-1
201+
>>> binary_search([1, 2, 4, 4, 4, 6, 7], 4)
202+
2
203+
>>> binary_search([0, 5, 7, 10, 10, 10], 10)
204+
3
199205
"""
200206
if any(a > b for a, b in pairwise(sorted_collection)):
201207
raise ValueError("sorted_collection must be sorted in ascending order")
202208
left = 0
203209
right = len(sorted_collection) - 1
210+
result = -1
204211

205212
while left <= right:
206213
midpoint = left + (right - left) // 2
207214
current_item = sorted_collection[midpoint]
208215
if current_item == item:
209-
return midpoint
216+
result = (
217+
midpoint # Found the item, but continue to find leftmost occurrence
218+
)
219+
right = midpoint - 1 # Look for more occurrences on the left
210220
elif item < current_item:
211221
right = midpoint - 1
212222
else:
213223
left = midpoint + 1
214-
return -1
224+
return result
215225

216226

217227
def binary_search_std_lib(sorted_collection: list[int], item: int) -> int:
@@ -328,7 +338,9 @@ def binary_search_by_recursion(
328338
329339
:param sorted_collection: some ascending sorted collection with comparable items
330340
:param item: item value to search
331-
:return: index of the found item or -1 if the item is not found
341+
:return: index of the found item or -1 if the item is not found.
342+
If there are multiple occurrences of the item, returns the index
343+
of the leftmost occurrence.
332344
333345
Examples:
334346
>>> binary_search_by_recursion([0, 5, 7, 10, 15], 0, 0, 4)
@@ -339,22 +351,35 @@ def binary_search_by_recursion(
339351
1
340352
>>> binary_search_by_recursion([0, 5, 7, 10, 15], 6, 0, 4)
341353
-1
354+
>>> binary_search_by_recursion([1, 2, 4, 4, 4, 6, 7], 4, 0, 6)
355+
2
356+
>>> binary_search_by_recursion([0, 5, 7, 10, 10, 10], 10, 0, 5)
357+
3
342358
"""
343359
if right < 0:
344360
right = len(sorted_collection) - 1
345361
if list(sorted_collection) != sorted(sorted_collection):
346362
raise ValueError("sorted_collection must be sorted in ascending order")
347-
if right < left:
348-
return -1
349363

350-
midpoint = left + (right - left) // 2
364+
# Helper function for the binary search
365+
def _binary_search_recursive(left_idx: int, right_idx: int) -> int:
366+
if right_idx < left_idx:
367+
return -1
351368

352-
if sorted_collection[midpoint] == item:
353-
return midpoint
354-
elif sorted_collection[midpoint] > item:
355-
return binary_search_by_recursion(sorted_collection, item, left, midpoint - 1)
356-
else:
357-
return binary_search_by_recursion(sorted_collection, item, midpoint + 1, right)
369+
midpoint = left_idx + (right_idx - left_idx) // 2
370+
current_item = sorted_collection[midpoint]
371+
372+
if current_item == item:
373+
# Found the item, now find the leftmost occurrence
374+
# First, recursively find any occurrence to the left
375+
leftmost = _binary_search_recursive(left_idx, midpoint - 1)
376+
return leftmost if leftmost != -1 else midpoint
377+
elif item < current_item:
378+
return _binary_search_recursive(left_idx, midpoint - 1)
379+
else:
380+
return _binary_search_recursive(midpoint + 1, right_idx)
381+
382+
return _binary_search_recursive(left, right)
358383

359384

360385
def exponential_search(sorted_collection: list[int], item: int) -> int:

0 commit comments

Comments
 (0)