Skip to content

Commit b988168

Browse files
author
SoulSniper1212
committed
Fix binary search to return leftmost occurrence for duplicates
Signed-off-by: SoulSniper1212 <warush23@gmail.com>
1 parent ae68a78 commit b988168

File tree

1 file changed

+36
-13
lines changed

1 file changed

+36
-13
lines changed

searches/binary_search.py

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,9 @@ def binary_search(sorted_collection: list[int], item: int) -> int:
186186
187187
:param sorted_collection: some ascending sorted collection with comparable items
188188
:param item: item value to search
189-
:return: index of the found item or -1 if the item is not found
189+
:return: index of the found item or -1 if the item is not found.
190+
If there are multiple occurrences of the item, returns the index
191+
of the leftmost occurrence.
190192
191193
Examples:
192194
>>> binary_search([0, 5, 7, 10, 15], 0)
@@ -197,22 +199,28 @@ def binary_search(sorted_collection: list[int], item: int) -> int:
197199
1
198200
>>> binary_search([0, 5, 7, 10, 15], 6)
199201
-1
202+
>>> binary_search([1, 2, 4, 4, 4, 6, 7], 4)
203+
2
204+
>>> binary_search([0, 5, 7, 10, 10, 10], 10)
205+
3
200206
"""
201207
if list(sorted_collection) != sorted(sorted_collection):
202208
raise ValueError("sorted_collection must be sorted in ascending order")
203209
left = 0
204210
right = len(sorted_collection) - 1
211+
result = -1
205212

206213
while left <= right:
207214
midpoint = left + (right - left) // 2
208215
current_item = sorted_collection[midpoint]
209216
if current_item == item:
210-
return midpoint
217+
result = midpoint # Found the item, but continue to find leftmost occurrence
218+
right = midpoint - 1 # Look for more occurrences on the left
211219
elif item < current_item:
212220
right = midpoint - 1
213221
else:
214222
left = midpoint + 1
215-
return -1
223+
return result
216224

217225

218226
def binary_search_std_lib(sorted_collection: list[int], item: int) -> int:
@@ -254,7 +262,9 @@ def binary_search_by_recursion(
254262
255263
:param sorted_collection: some ascending sorted collection with comparable items
256264
:param item: item value to search
257-
:return: index of the found item or -1 if the item is not found
265+
:return: index of the found item or -1 if the item is not found.
266+
If there are multiple occurrences of the item, returns the index
267+
of the leftmost occurrence.
258268
259269
Examples:
260270
>>> binary_search_by_recursion([0, 5, 7, 10, 15], 0, 0, 4)
@@ -265,22 +275,35 @@ def binary_search_by_recursion(
265275
1
266276
>>> binary_search_by_recursion([0, 5, 7, 10, 15], 6, 0, 4)
267277
-1
278+
>>> binary_search_by_recursion([1, 2, 4, 4, 4, 6, 7], 4, 0, 6)
279+
2
280+
>>> binary_search_by_recursion([0, 5, 7, 10, 10, 10], 10, 0, 5)
281+
3
268282
"""
269283
if right < 0:
270284
right = len(sorted_collection) - 1
271285
if list(sorted_collection) != sorted(sorted_collection):
272286
raise ValueError("sorted_collection must be sorted in ascending order")
273-
if right < left:
274-
return -1
287+
288+
# Helper function for the binary search
289+
def _binary_search_recursive(left_idx: int, right_idx: int) -> int:
290+
if right_idx < left_idx:
291+
return -1
275292

276-
midpoint = left + (right - left) // 2
293+
midpoint = left_idx + (right_idx - left_idx) // 2
294+
current_item = sorted_collection[midpoint]
277295

278-
if sorted_collection[midpoint] == item:
279-
return midpoint
280-
elif sorted_collection[midpoint] > item:
281-
return binary_search_by_recursion(sorted_collection, item, left, midpoint - 1)
282-
else:
283-
return binary_search_by_recursion(sorted_collection, item, midpoint + 1, right)
296+
if current_item == item:
297+
# Found the item, now find the leftmost occurrence
298+
# First, recursively find any occurrence to the left
299+
leftmost = _binary_search_recursive(left_idx, midpoint - 1)
300+
return leftmost if leftmost != -1 else midpoint
301+
elif item < current_item:
302+
return _binary_search_recursive(left_idx, midpoint - 1)
303+
else:
304+
return _binary_search_recursive(midpoint + 1, right_idx)
305+
306+
return _binary_search_recursive(left, right)
284307

285308

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

0 commit comments

Comments
 (0)