@@ -195,6 +195,8 @@ def binary_search(sorted_collection: list[int], item: int) -> int:
195195 4
196196 >>> binary_search([0, 5, 7, 10, 15], 5)
197197 1
198+ >>> binary_search([0, 5, 6, 7, 7, 8, 10, 10, 10, 13, 15], 10)
199+ 6
198200 >>> binary_search([0, 5, 7, 10, 15], 6)
199201 -1
200202 """
@@ -207,6 +209,9 @@ def binary_search(sorted_collection: list[int], item: int) -> int:
207209 midpoint = left + (right - left ) // 2
208210 current_item = sorted_collection [midpoint ]
209211 if current_item == item :
212+ # Found a match; now move left to locate the first occurrence
213+ while midpoint > 0 and sorted_collection [midpoint - 1 ] == item :
214+ midpoint -= 1
210215 return midpoint
211216 elif item < current_item :
212217 right = midpoint - 1
@@ -232,6 +237,8 @@ def binary_search_std_lib(sorted_collection: list[int], item: int) -> int:
232237 4
233238 >>> binary_search_std_lib([0, 5, 7, 10, 15], 5)
234239 1
240+ >>> binary_search_std_lib([0, 5, 6, 7, 7, 8, 10, 10, 10, 13, 15], 10)
241+ 6
235242 >>> binary_search_std_lib([0, 5, 7, 10, 15], 6)
236243 -1
237244 """
@@ -263,19 +270,30 @@ def binary_search_by_recursion(
263270 4
264271 >>> binary_search_by_recursion([0, 5, 7, 10, 15], 5, 0, 4)
265272 1
273+ >>> binary_search_by_recursion([0, 5, 6, 7, 7, 8, 10, 10, 10, 13, 15], 10)
274+ 6
266275 >>> binary_search_by_recursion([0, 5, 7, 10, 15], 6, 0, 4)
267276 -1
268277 """
278+ if (
279+ left == 0
280+ and right == len (sorted_collection ) - 1
281+ and list (sorted_collection ) != sorted (sorted_collection )
282+ ): # only verify ascending once
283+ raise ValueError ("sorted_collection must be sorted in ascending order" )
284+
269285 if right < 0 :
270286 right = len (sorted_collection ) - 1
271- if list (sorted_collection ) != sorted (sorted_collection ):
272- raise ValueError ("sorted_collection must be sorted in ascending order" )
287+
273288 if right < left :
274289 return - 1
275290
276291 midpoint = left + (right - left ) // 2
277292
278293 if sorted_collection [midpoint ] == item :
294+ # Found a match; now move left to locate the first occurrence
295+ while midpoint > 0 and sorted_collection [midpoint - 1 ] == item :
296+ midpoint -= 1
279297 return midpoint
280298 elif sorted_collection [midpoint ] > item :
281299 return binary_search_by_recursion (sorted_collection , item , left , midpoint - 1 )
@@ -304,6 +322,8 @@ def exponential_search(sorted_collection: list[int], item: int) -> int:
304322 4
305323 >>> exponential_search([0, 5, 7, 10, 15], 5)
306324 1
325+ >>> exponential_search([0, 5, 6, 7, 7, 8, 10, 10, 10, 13, 15], 10)
326+ 6
307327 >>> exponential_search([0, 5, 7, 10, 15], 6)
308328 -1
309329 """
@@ -335,9 +355,20 @@ def exponential_search(sorted_collection: list[int], item: int) -> int:
335355 import timeit
336356
337357 doctest .testmod ()
338- for search in searches :
339- name = f"{ search .__name__ :>26} "
340- print (f"{ name } : { search ([0 , 5 , 7 , 10 , 15 ], 10 ) = } " ) # type: ignore[operator]
358+
359+ test_cases = [
360+ ([0 , 5 , 7 , 10 , 15 ], 10 , 3 ),
361+ ([0 , 5 , 6 , 7 , 7 , 8 , 10 , 10 , 10 , 13 , 15 ], 10 , 6 ),
362+ ([0 , 0 , 0 , 5 , 6 , 7 , 7 , 8 , 10 , 10 , 10 , 13 , 15 ], 0 , 0 ),
363+ ]
364+
365+ for items , target , answer in test_cases :
366+ print (f"search({ items } , { target } )" )
367+ for search in searches :
368+ name = f"{ search .__name__ :>26} "
369+ res = search (items , target )
370+ mark = "✅" if res == answer else "❌"
371+ print (f"{ mark } { name } : { res } " )
341372
342373 print ("\n Benchmarks..." )
343374 setup = "collection = range(1000)"
0 commit comments