Skip to content

Commit 37fe148

Browse files
Fix binary search with duplicates issue #13886
1 parent a051ab5 commit 37fe148

File tree

1 file changed

+65
-0
lines changed

1 file changed

+65
-0
lines changed

searches/binary_search.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,71 @@ def binary_search_std_lib(sorted_collection: list[int], item: int) -> int:
243243
return -1
244244

245245

246+
def binary_search_with_duplicates(sorted_collection: list[int], item: int) -> list[int]:
247+
"""Pure implementation of a binary search algorithm in Python that supports
248+
duplicates.
249+
250+
Resources used:
251+
https://stackoverflow.com/questions/13197552/using-binary-search-with-sorted-array-with-duplicates
252+
253+
The collection must be sorted in ascending order; otherwise the result will be
254+
unpredictable. If the target appears multiple times, this function returns a
255+
list of **all** indexes where the target occurs. If the target is not found,
256+
it returns an empty list.
257+
258+
:param sorted_collection: some ascending sorted collection with comparable items
259+
:param item: item value to search for
260+
:return: a list of indexes where the item is found (empty list if not found)
261+
262+
Examples:
263+
>>> binary_search_with_duplicates([0, 5, 7, 10, 15], 0)
264+
[0]
265+
>>> binary_search_with_duplicates([0, 5, 7, 10, 15], 15)
266+
[4]
267+
>>> binary_search_with_duplicates([1, 2, 2, 2, 3], 2)
268+
[1, 2, 3]
269+
>>> binary_search_with_duplicates([1, 2, 2, 2, 3], 4)
270+
[]
271+
"""
272+
if list(sorted_collection) != sorted(sorted_collection):
273+
raise ValueError("sorted_collection must be sorted in ascending order")
274+
275+
"""find lower bounds"""
276+
277+
def lower_bound(sorted_collection: list[int], item: int) -> int:
278+
left = 0
279+
right = len(sorted_collection)
280+
while left < right:
281+
midpoint = left + (right - left) // 2
282+
current_item = sorted_collection[midpoint]
283+
if current_item < item:
284+
left = midpoint + 1
285+
else:
286+
right = midpoint
287+
return left
288+
289+
"""find upper bounds"""
290+
291+
def upper_bound(sorted_collection: list[int], item: int) -> int:
292+
left = 0
293+
right = len(sorted_collection)
294+
while left < right:
295+
midpoint = left + (right - left) // 2
296+
current_item = sorted_collection[midpoint]
297+
if current_item <= item:
298+
left = midpoint + 1
299+
else:
300+
right = midpoint
301+
return left
302+
303+
left = lower_bound(sorted_collection, item)
304+
right = upper_bound(sorted_collection, item)
305+
306+
if left == len(sorted_collection) or sorted_collection[left] != item:
307+
return []
308+
return list(range(left, right))
309+
310+
246311
def binary_search_by_recursion(
247312
sorted_collection: list[int], item: int, left: int = 0, right: int = -1
248313
) -> int:

0 commit comments

Comments
 (0)