@@ -21,9 +21,6 @@ def bisect_left(
2121 Locates the first element in a sorted array that is larger or equal to a given
2222 value.
2323
24- It has the same interface as
25- https://docs.python.org/3/library/bisect.html#bisect.bisect_left .
26-
2724 :param sorted_collection: some ascending sorted collection with comparable items
2825 :param item: item to bisect
2926 :param lo: lowest index to consider (as in sorted_collection[lo:hi])
@@ -62,27 +59,13 @@ def bisect_right(
6259 """
6360 Locates the first element in a sorted array that is larger than a given value.
6461
65- It has the same interface as
66- https://docs.python.org/3/library/bisect.html#bisect.bisect_right .
67-
68- :param sorted_collection: some ascending sorted collection with comparable items
69- :param item: item to bisect
70- :param lo: lowest index to consider (as in sorted_collection[lo:hi])
71- :param hi: past the highest index to consider (as in sorted_collection[lo:hi])
72- :return: index i such that all values in sorted_collection[lo:i] are <= item and
73- all values in sorted_collection[i:hi] are > item.
74-
7562 Examples:
7663 >>> bisect_right([0, 5, 7, 10, 15], 0)
7764 1
7865 >>> bisect_right([0, 5, 7, 10, 15], 15)
7966 5
8067 >>> bisect_right([0, 5, 7, 10, 15], 6)
8168 2
82- >>> bisect_right([0, 5, 7, 10, 15], 15, 1, 3)
83- 3
84- >>> bisect_right([0, 5, 7, 10, 15], 6, 2)
85- 2
8669 """
8770 if hi < 0 :
8871 hi = len (sorted_collection )
@@ -103,9 +86,6 @@ def insort_left(
10386 """
10487 Inserts a given value into a sorted array before other values with the same value.
10588
106- It has the same interface as
107- https://docs.python.org/3/library/bisect.html#bisect.insort_left .
108-
10989 Examples:
11090 >>> sorted_collection = [0, 5, 7, 10, 15]
11191 >>> insort_left(sorted_collection, 6)
@@ -121,9 +101,6 @@ def insort_right(
121101 """
122102 Inserts a given value into a sorted array after other values with the same value.
123103
124- It has the same interface as
125- https://docs.python.org/3/library/bisect.html#bisect.insort_right .
126-
127104 Examples:
128105 >>> sorted_collection = [0, 5, 7, 10, 15]
129106 >>> insort_right(sorted_collection, 6)
@@ -135,7 +112,7 @@ def insort_right(
135112
136113def binary_search (sorted_collection : list [int ], item : int ) -> int :
137114 """Pure implementation of a binary search algorithm in Python.
138- Finds the first occurrence of the item.
115+ Updated to find the first occurrence of the item.
139116
140117 :param sorted_collection: some ascending sorted collection with comparable items
141118 :param item: item value to search
@@ -153,6 +130,7 @@ def binary_search(sorted_collection: list[int], item: int) -> int:
153130 """
154131 if any (a > b for a , b in pairwise (sorted_collection )):
155132 raise ValueError ("sorted_collection must be sorted in ascending order" )
133+
156134 left = 0
157135 right = len (sorted_collection ) - 1
158136 result = - 1
@@ -170,42 +148,31 @@ def binary_search(sorted_collection: list[int], item: int) -> int:
170148
171149
172150def binary_search_std_lib (sorted_collection : list [int ], item : int ) -> int :
173- """Implementation of a binary search algorithm using stdlib"""
174- if list (sorted_collection ) != sorted (sorted_collection ):
175- raise ValueError ("sorted_collection must be sorted in ascending order" )
151+ """Binary search algorithm in Python using stdlib.
152+ Finds the first occurrence.
153+
154+ >>> binary_search_std_lib([0, 5, 7, 10, 15], 0)
155+ 0
156+ >>> binary_search_std_lib([1, 2, 2, 2, 3], 2)
157+ 1
158+ """
176159 index = bisect .bisect_left (sorted_collection , item )
177160 if index != len (sorted_collection ) and sorted_collection [index ] == item :
178161 return index
179162 return - 1
180163
181164
182165def binary_search_with_duplicates (sorted_collection : list [int ], item : int ) -> list [int ]:
183- """Returns a list of all indexes where the target occurs."""
184- if list (sorted_collection ) != sorted (sorted_collection ):
185- raise ValueError ("sorted_collection must be sorted in ascending order" )
166+ """Returns a list of all indexes where the target occurs.
186167
187- def lower_bound (sorted_collection : list [int ], item : int ) -> int :
188- left , right = 0 , len (sorted_collection )
189- while left < right :
190- midpoint = left + (right - left ) // 2
191- if sorted_collection [midpoint ] < item :
192- left = midpoint + 1
193- else :
194- right = midpoint
195- return left
196-
197- def upper_bound (sorted_collection : list [int ], item : int ) -> int :
198- left , right = 0 , len (sorted_collection )
199- while left < right :
200- midpoint = left + (right - left ) // 2
201- if sorted_collection [midpoint ] <= item :
202- left = midpoint + 1
203- else :
204- right = midpoint
205- return left
206-
207- left = lower_bound (sorted_collection , item )
208- right = upper_bound (sorted_collection , item )
168+ Examples:
169+ >>> binary_search_with_duplicates([1, 2, 2, 2, 3], 2)
170+ [1, 2, 3]
171+ >>> binary_search_with_duplicates([1, 2, 2, 2, 3], 4)
172+ []
173+ """
174+ left = bisect_left (sorted_collection , item )
175+ right = bisect_right (sorted_collection , item )
209176
210177 if left == len (sorted_collection ) or sorted_collection [left ] != item :
211178 return []
@@ -215,30 +182,41 @@ def upper_bound(sorted_collection: list[int], item: int) -> int:
215182def binary_search_by_recursion (
216183 sorted_collection : list [int ], item : int , left : int = 0 , right : int = - 1
217184) -> int :
218- """Recursive binary search finding the first occurrence."""
185+ """
186+ Recursive binary search finding the first occurrence.
187+ """
219188 if right < 0 :
220189 right = len (sorted_collection ) - 1
221- if list ( sorted_collection ) != sorted ( sorted_collection ):
222- raise ValueError ( "sorted_collection must be sorted in ascending order" )
190+
191+ # Base case: range is empty
223192 if right < left :
224193 return - 1
225194
226195 midpoint = left + (right - left ) // 2
227196
228197 if sorted_collection [midpoint ] == item :
229- # Check if there is an occurrence to the left
230- res = binary_search_by_recursion (sorted_collection , item , left , midpoint - 1 )
231- return res if res != - 1 else midpoint
198+ # We found a match! Now see if there's an earlier one to the left.
199+ # CRITICAL: Only recurse if there is actually space to the left
200+ if midpoint > left :
201+ res = binary_search_by_recursion (sorted_collection , item , left , midpoint - 1 )
202+ return res if res != - 1 else midpoint
203+ return midpoint
204+
232205 elif sorted_collection [midpoint ] > item :
233206 return binary_search_by_recursion (sorted_collection , item , left , midpoint - 1 )
234207 else :
235208 return binary_search_by_recursion (sorted_collection , item , midpoint + 1 , right )
236209
237210
238211def exponential_search (sorted_collection : list [int ], item : int ) -> int :
239- """Implementation of an exponential search algorithm."""
240- if list (sorted_collection ) != sorted (sorted_collection ):
241- raise ValueError ("sorted_collection must be sorted in ascending order" )
212+ """Implementation of an exponential search algorithm finding the first occurrence.
213+
214+ Examples:
215+ >>> exponential_search([0, 5, 7, 10, 15], 0)
216+ 0
217+ >>> exponential_search([1, 2, 2, 2, 3], 2)
218+ 1
219+ """
242220 if not sorted_collection :
243221 return - 1
244222 bound = 1
@@ -273,6 +251,6 @@ def exponential_search(sorted_collection: list[int], item: int) -> int:
273251 print (
274252 f"{ name :>26} :" ,
275253 timeit .timeit (
276- f"{ name } (collection, 500)" , setup = setup , number = 5_000 , globals = globals ()
254+ f"{ name } (list( collection) , 500)" , setup = setup , number = 5_000 , globals = globals ()
277255 ),
278- )
256+ )
0 commit comments