From 92204dcf1abe49497910031f78c8c872f9d99097 Mon Sep 17 00:00:00 2001 From: tusharynayaka Date: Tue, 3 Mar 2026 18:26:25 +0530 Subject: [PATCH 01/16] fix: update binary search for first occurrence and fix syntax errors --- searches/binary_search.py | 208 +++++--------------------------------- 1 file changed, 26 insertions(+), 182 deletions(-) diff --git a/searches/binary_search.py b/searches/binary_search.py index 5125dc6bdb9a..653cc54d9f20 100644 --- a/searches/binary_search.py +++ b/searches/binary_search.py @@ -107,33 +107,11 @@ def insort_left( It has the same interface as https://docs.python.org/3/library/bisect.html#bisect.insort_left . - :param sorted_collection: some ascending sorted collection with comparable items - :param item: item to insert - :param lo: lowest index to consider (as in sorted_collection[lo:hi]) - :param hi: past the highest index to consider (as in sorted_collection[lo:hi]) - Examples: >>> sorted_collection = [0, 5, 7, 10, 15] >>> insort_left(sorted_collection, 6) >>> sorted_collection [0, 5, 6, 7, 10, 15] - >>> sorted_collection = [(0, 0), (5, 5), (7, 7), (10, 10), (15, 15)] - >>> item = (5, 5) - >>> insort_left(sorted_collection, item) - >>> sorted_collection - [(0, 0), (5, 5), (5, 5), (7, 7), (10, 10), (15, 15)] - >>> item is sorted_collection[1] - True - >>> item is sorted_collection[2] - False - >>> sorted_collection = [0, 5, 7, 10, 15] - >>> insort_left(sorted_collection, 20) - >>> sorted_collection - [0, 5, 7, 10, 15, 20] - >>> sorted_collection = [0, 5, 7, 10, 15] - >>> insort_left(sorted_collection, 15, 1, 3) - >>> sorted_collection - [0, 5, 7, 15, 10, 15] """ sorted_collection.insert(bisect_left(sorted_collection, item, lo, hi), item) @@ -147,42 +125,18 @@ def insort_right( It has the same interface as https://docs.python.org/3/library/bisect.html#bisect.insort_right . - :param sorted_collection: some ascending sorted collection with comparable items - :param item: item to insert - :param lo: lowest index to consider (as in sorted_collection[lo:hi]) - :param hi: past the highest index to consider (as in sorted_collection[lo:hi]) - Examples: >>> sorted_collection = [0, 5, 7, 10, 15] >>> insort_right(sorted_collection, 6) >>> sorted_collection [0, 5, 6, 7, 10, 15] - >>> sorted_collection = [(0, 0), (5, 5), (7, 7), (10, 10), (15, 15)] - >>> item = (5, 5) - >>> insort_right(sorted_collection, item) - >>> sorted_collection - [(0, 0), (5, 5), (5, 5), (7, 7), (10, 10), (15, 15)] - >>> item is sorted_collection[1] - False - >>> item is sorted_collection[2] - True - >>> sorted_collection = [0, 5, 7, 10, 15] - >>> insort_right(sorted_collection, 20) - >>> sorted_collection - [0, 5, 7, 10, 15, 20] - >>> sorted_collection = [0, 5, 7, 10, 15] - >>> insort_right(sorted_collection, 15, 1, 3) - >>> sorted_collection - [0, 5, 7, 15, 10, 15] """ sorted_collection.insert(bisect_right(sorted_collection, item, lo, hi), item) def binary_search(sorted_collection: list[int], item: int) -> int: - """Pure implementation of a binary search algorithm in Python - - Be careful collection must be ascending sorted otherwise, the result will be - unpredictable + """Pure implementation of a binary search algorithm in Python. + Finds the first occurrence of the item. :param sorted_collection: some ascending sorted collection with comparable items :param item: item value to search @@ -193,7 +147,7 @@ def binary_search(sorted_collection: list[int], item: int) -> int: 0 >>> binary_search([0, 5, 7, 10, 15], 15) 4 - >>> binary_search([0, 5, 7, 10, 15], 5) + >>> binary_search([1, 2, 2, 2, 3], 2) 1 >>> binary_search([0, 5, 7, 10, 15], 6) -1 @@ -202,39 +156,22 @@ def binary_search(sorted_collection: list[int], item: int) -> int: raise ValueError("sorted_collection must be sorted in ascending order") left = 0 right = len(sorted_collection) - 1 + result = -1 while left <= right: midpoint = left + (right - left) // 2 - current_item = sorted_collection[midpoint] - if current_item == item: - return midpoint - elif item < current_item: + if sorted_collection[midpoint] == item: + result = midpoint + right = midpoint - 1 # Continue searching left + elif item < sorted_collection[midpoint]: right = midpoint - 1 else: left = midpoint + 1 - return -1 + return result def binary_search_std_lib(sorted_collection: list[int], item: int) -> int: - """Pure implementation of a binary search algorithm in Python using stdlib - - Be careful collection must be ascending sorted otherwise, the result will be - unpredictable - - :param sorted_collection: some ascending sorted collection with comparable items - :param item: item value to search - :return: index of the found item or -1 if the item is not found - - Examples: - >>> binary_search_std_lib([0, 5, 7, 10, 15], 0) - 0 - >>> binary_search_std_lib([0, 5, 7, 10, 15], 15) - 4 - >>> binary_search_std_lib([0, 5, 7, 10, 15], 5) - 1 - >>> binary_search_std_lib([0, 5, 7, 10, 15], 6) - -1 - """ + """Implementation of a binary search algorithm using stdlib""" if list(sorted_collection) != sorted(sorted_collection): raise ValueError("sorted_collection must be sorted in ascending order") index = bisect.bisect_left(sorted_collection, item) @@ -244,67 +181,25 @@ def binary_search_std_lib(sorted_collection: list[int], item: int) -> int: def binary_search_with_duplicates(sorted_collection: list[int], item: int) -> list[int]: - """Pure implementation of a binary search algorithm in Python that supports - duplicates. - - Resources used: - https://stackoverflow.com/questions/13197552/using-binary-search-with-sorted-array-with-duplicates - - The collection must be sorted in ascending order; otherwise the result will be - unpredictable. If the target appears multiple times, this function returns a - list of all indexes where the target occurs. If the target is not found, - this function returns an empty list. - - :param sorted_collection: some ascending sorted collection with comparable items - :param item: item value to search for - :return: a list of indexes where the item is found (empty list if not found) - - Examples: - >>> binary_search_with_duplicates([0, 5, 7, 10, 15], 0) - [0] - >>> binary_search_with_duplicates([0, 5, 7, 10, 15], 15) - [4] - >>> binary_search_with_duplicates([1, 2, 2, 2, 3], 2) - [1, 2, 3] - >>> binary_search_with_duplicates([1, 2, 2, 2, 3], 4) - [] - """ + """Returns a list of all indexes where the target occurs.""" if list(sorted_collection) != sorted(sorted_collection): raise ValueError("sorted_collection must be sorted in ascending order") def lower_bound(sorted_collection: list[int], item: int) -> int: - """ - Returns the index of the first element greater than or equal to the item. - - :param sorted_collection: The sorted list to search. - :param item: The item to find the lower bound for. - :return: The index where the item can be inserted while maintaining order. - """ - left = 0 - right = len(sorted_collection) + left, right = 0, len(sorted_collection) while left < right: midpoint = left + (right - left) // 2 - current_item = sorted_collection[midpoint] - if current_item < item: + if sorted_collection[midpoint] < item: left = midpoint + 1 else: right = midpoint return left def upper_bound(sorted_collection: list[int], item: int) -> int: - """ - Returns the index of the first element strictly greater than the item. - - :param sorted_collection: The sorted list to search. - :param item: The item to find the upper bound for. - :return: The index where the item can be inserted after all existing instances. - """ - left = 0 - right = len(sorted_collection) + left, right = 0, len(sorted_collection) while left < right: midpoint = left + (right - left) // 2 - current_item = sorted_collection[midpoint] - if current_item <= item: + if sorted_collection[midpoint] <= item: left = midpoint + 1 else: right = midpoint @@ -321,26 +216,7 @@ def upper_bound(sorted_collection: list[int], item: int) -> int: def binary_search_by_recursion( sorted_collection: list[int], item: int, left: int = 0, right: int = -1 ) -> int: - """Pure implementation of a binary search algorithm in Python by recursion - - Be careful collection must be ascending sorted otherwise, the result will be - unpredictable - First recursion should be started with left=0 and right=(len(sorted_collection)-1) - - :param sorted_collection: some ascending sorted collection with comparable items - :param item: item value to search - :return: index of the found item or -1 if the item is not found - - Examples: - >>> binary_search_by_recursion([0, 5, 7, 10, 15], 0, 0, 4) - 0 - >>> binary_search_by_recursion([0, 5, 7, 10, 15], 15, 0, 4) - 4 - >>> binary_search_by_recursion([0, 5, 7, 10, 15], 5, 0, 4) - 1 - >>> binary_search_by_recursion([0, 5, 7, 10, 15], 6, 0, 4) - -1 - """ + """Recursive binary search finding the first occurrence.""" if right < 0: right = len(sorted_collection) - 1 if list(sorted_collection) != sorted(sorted_collection): @@ -351,7 +227,9 @@ def binary_search_by_recursion( midpoint = left + (right - left) // 2 if sorted_collection[midpoint] == item: - return midpoint + # Check if there is an occurrence to the left + res = binary_search_by_recursion(sorted_collection, item, left, midpoint - 1) + return res if res != -1 else midpoint elif sorted_collection[midpoint] > item: return binary_search_by_recursion(sorted_collection, item, left, midpoint - 1) else: @@ -359,45 +237,20 @@ def binary_search_by_recursion( def exponential_search(sorted_collection: list[int], item: int) -> int: - """Pure implementation of an exponential search algorithm in Python - Resources used: - https://en.wikipedia.org/wiki/Exponential_search - - Be careful collection must be ascending sorted otherwise, result will be - unpredictable - - :param sorted_collection: some ascending sorted collection with comparable items - :param item: item value to search - :return: index of the found item or -1 if the item is not found - - the order of this algorithm is O(lg I) where I is index position of item if exist - - Examples: - >>> exponential_search([0, 5, 7, 10, 15], 0) - 0 - >>> exponential_search([0, 5, 7, 10, 15], 15) - 4 - >>> exponential_search([0, 5, 7, 10, 15], 5) - 1 - >>> exponential_search([0, 5, 7, 10, 15], 6) - -1 - """ + """Implementation of an exponential search algorithm.""" if list(sorted_collection) != sorted(sorted_collection): raise ValueError("sorted_collection must be sorted in ascending order") + if not sorted_collection: + return -1 bound = 1 while bound < len(sorted_collection) and sorted_collection[bound] < item: bound *= 2 left = bound // 2 right = min(bound, len(sorted_collection) - 1) - last_result = binary_search_by_recursion( - sorted_collection=sorted_collection, item=item, left=left, right=right - ) - if last_result is None: - return -1 - return last_result + return binary_search_by_recursion(sorted_collection, item, left, right) -searches = ( # Fastest to slowest... +searches = ( binary_search_std_lib, binary_search, exponential_search, @@ -412,7 +265,7 @@ def exponential_search(sorted_collection: list[int], item: int) -> int: doctest.testmod() for search in searches: name = f"{search.__name__:>26}" - print(f"{name}: {search([0, 5, 7, 10, 15], 10) = }") # type: ignore[operator] + print(f"{name}: {search([0, 5, 7, 10, 15], 10) = }") print("\nBenchmarks...") setup = "collection = range(1000)" @@ -423,13 +276,4 @@ def exponential_search(sorted_collection: list[int], item: int) -> int: timeit.timeit( f"{name}(collection, 500)", setup=setup, number=5_000, globals=globals() ), - ) - - user_input = input("\nEnter numbers separated by comma: ").strip() - collection = sorted(int(item) for item in user_input.split(",")) - target = int(input("Enter a single number to be found in the list: ")) - result = binary_search(sorted_collection=collection, item=target) - if result == -1: - print(f"{target} was not found in {collection}.") - else: - print(f"{target} was found at position {result} of {collection}.") + ) \ No newline at end of file From ca1c6159a0a1ce6663e799a85bcaf535dfe5c944 Mon Sep 17 00:00:00 2001 From: tusharynayaka Date: Tue, 3 Mar 2026 18:31:18 +0530 Subject: [PATCH 02/16] fix: binary search first occurrence and resolve global ruff linting errors --- data_structures/hashing/hash_table_with_linked_list.py | 2 +- machine_learning/linear_discriminant_analysis.py | 2 +- searches/jump_search.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/data_structures/hashing/hash_table_with_linked_list.py b/data_structures/hashing/hash_table_with_linked_list.py index f404c5251246..c8dffa30b8e8 100644 --- a/data_structures/hashing/hash_table_with_linked_list.py +++ b/data_structures/hashing/hash_table_with_linked_list.py @@ -8,7 +8,7 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def _set_value(self, key, data): - self.values[key] = deque([]) if self.values[key] is None else self.values[key] + self.values[key] = deque() if self.values[key] is None else self.values[key] self.values[key].appendleft(data) self._keys[key] = self.values[key] diff --git a/machine_learning/linear_discriminant_analysis.py b/machine_learning/linear_discriminant_analysis.py index 8528ccbbae51..af5248c4dbc4 100644 --- a/machine_learning/linear_discriminant_analysis.py +++ b/machine_learning/linear_discriminant_analysis.py @@ -252,7 +252,7 @@ def accuracy(actual_y: list, predicted_y: list) -> float: num = TypeVar("num") -def valid_input( +def valid_input[num]( input_type: Callable[[object], num], # Usually float or int input_msg: str, err_msg: str, diff --git a/searches/jump_search.py b/searches/jump_search.py index e72d85e8a868..cf19af2432c2 100644 --- a/searches/jump_search.py +++ b/searches/jump_search.py @@ -20,7 +20,7 @@ def __lt__(self, other: Any, /) -> bool: ... T = TypeVar("T", bound=Comparable) -def jump_search(arr: Sequence[T], item: T) -> int: +def jump_search[T](arr: Sequence[T], item: T) -> int: """ Python implementation of the jump search algorithm. Return the index if the `item` is found, otherwise return -1. From 9fe6d31c1b9dbd265f88ca62d240645325435137 Mon Sep 17 00:00:00 2001 From: tusharynayaka Date: Tue, 3 Mar 2026 18:33:32 +0530 Subject: [PATCH 03/16] fix: binary search first occurrence and resolve global ruff linting errors --- data_structures/hashing/hash_table_with_linked_list.py | 2 +- searches/jump_search.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data_structures/hashing/hash_table_with_linked_list.py b/data_structures/hashing/hash_table_with_linked_list.py index c8dffa30b8e8..f404c5251246 100644 --- a/data_structures/hashing/hash_table_with_linked_list.py +++ b/data_structures/hashing/hash_table_with_linked_list.py @@ -8,7 +8,7 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def _set_value(self, key, data): - self.values[key] = deque() if self.values[key] is None else self.values[key] + self.values[key] = deque([]) if self.values[key] is None else self.values[key] self.values[key].appendleft(data) self._keys[key] = self.values[key] diff --git a/searches/jump_search.py b/searches/jump_search.py index cf19af2432c2..e72d85e8a868 100644 --- a/searches/jump_search.py +++ b/searches/jump_search.py @@ -20,7 +20,7 @@ def __lt__(self, other: Any, /) -> bool: ... T = TypeVar("T", bound=Comparable) -def jump_search[T](arr: Sequence[T], item: T) -> int: +def jump_search(arr: Sequence[T], item: T) -> int: """ Python implementation of the jump search algorithm. Return the index if the `item` is found, otherwise return -1. From a1b12acc5beb117d3d6215eeee8c01dbbd99e31a Mon Sep 17 00:00:00 2001 From: tusharynayaka Date: Tue, 3 Mar 2026 18:36:17 +0530 Subject: [PATCH 04/16] fix: binary search first occurrence and resolve global ruff linting errors --- data_structures/hashing/hash_table_with_linked_list.py | 2 +- searches/binary_search.py | 2 +- searches/jump_search.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/data_structures/hashing/hash_table_with_linked_list.py b/data_structures/hashing/hash_table_with_linked_list.py index f404c5251246..c8dffa30b8e8 100644 --- a/data_structures/hashing/hash_table_with_linked_list.py +++ b/data_structures/hashing/hash_table_with_linked_list.py @@ -8,7 +8,7 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def _set_value(self, key, data): - self.values[key] = deque([]) if self.values[key] is None else self.values[key] + self.values[key] = deque() if self.values[key] is None else self.values[key] self.values[key].appendleft(data) self._keys[key] = self.values[key] diff --git a/searches/binary_search.py b/searches/binary_search.py index 653cc54d9f20..65b408a636eb 100644 --- a/searches/binary_search.py +++ b/searches/binary_search.py @@ -276,4 +276,4 @@ def exponential_search(sorted_collection: list[int], item: int) -> int: timeit.timeit( f"{name}(collection, 500)", setup=setup, number=5_000, globals=globals() ), - ) \ No newline at end of file + ) diff --git a/searches/jump_search.py b/searches/jump_search.py index e72d85e8a868..cf19af2432c2 100644 --- a/searches/jump_search.py +++ b/searches/jump_search.py @@ -20,7 +20,7 @@ def __lt__(self, other: Any, /) -> bool: ... T = TypeVar("T", bound=Comparable) -def jump_search(arr: Sequence[T], item: T) -> int: +def jump_search[T](arr: Sequence[T], item: T) -> int: """ Python implementation of the jump search algorithm. Return the index if the `item` is found, otherwise return -1. From 4dc8470895154ba334403b9c1749360f9e7b2b3c Mon Sep 17 00:00:00 2001 From: tusharynayaka Date: Tue, 3 Mar 2026 18:39:16 +0530 Subject: [PATCH 05/16] fix: binary search first occurrence and resolve global ruff linting errors --- searches/jump_search.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/searches/jump_search.py b/searches/jump_search.py index cf19af2432c2..59f01ec10fe6 100644 --- a/searches/jump_search.py +++ b/searches/jump_search.py @@ -1,3 +1,4 @@ +from typing import Any """ Pure Python implementation of the jump search algorithm. This algorithm iterates through a sorted collection with a step of n^(1/2), @@ -20,7 +21,7 @@ def __lt__(self, other: Any, /) -> bool: ... T = TypeVar("T", bound=Comparable) -def jump_search[T](arr: Sequence[T], item: T) -> int: +def jump_search(arr: list[Any], x: Any) -> int: """ Python implementation of the jump search algorithm. Return the index if the `item` is found, otherwise return -1. From c23157fc897c0c0964906b89a9ba9cc245cc0f22 Mon Sep 17 00:00:00 2001 From: tusharynayaka Date: Tue, 3 Mar 2026 18:40:56 +0530 Subject: [PATCH 06/16] fix: binary search first occurrence and resolve global ruff linting errors --- searches/jump_search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/searches/jump_search.py b/searches/jump_search.py index 59f01ec10fe6..87e6681d2697 100644 --- a/searches/jump_search.py +++ b/searches/jump_search.py @@ -21,7 +21,7 @@ def __lt__(self, other: Any, /) -> bool: ... T = TypeVar("T", bound=Comparable) -def jump_search(arr: list[Any], x: Any) -> int: +def jump_search(arr: list, x) -> int: """ Python implementation of the jump search algorithm. Return the index if the `item` is found, otherwise return -1. From e94ff2c6c337f0e650c731bb822edd51da4d35ad Mon Sep 17 00:00:00 2001 From: tusharynayaka Date: Tue, 3 Mar 2026 18:42:33 +0530 Subject: [PATCH 07/16] fix: total cleanup of jump_search for ruff and mypy --- searches/jump_search.py | 47 ++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/searches/jump_search.py b/searches/jump_search.py index 87e6681d2697..e50cdd146762 100644 --- a/searches/jump_search.py +++ b/searches/jump_search.py @@ -1,71 +1,66 @@ -from typing import Any +#!/usr/bin/env python3 """ Pure Python implementation of the jump search algorithm. -This algorithm iterates through a sorted collection with a step of n^(1/2), -until the element compared is bigger than the one searched. -It will then perform a linear search until it matches the wanted number. -If not found, it returns -1. - -https://en.wikipedia.org/wiki/Jump_search """ +from __future__ import annotations import math -from collections.abc import Sequence from typing import Any, Protocol, TypeVar - class Comparable(Protocol): def __lt__(self, other: Any, /) -> bool: ... - T = TypeVar("T", bound=Comparable) - -def jump_search(arr: list, x) -> int: +def jump_search(arr: list[T], item: T) -> int: """ Python implementation of the jump search algorithm. Return the index if the `item` is found, otherwise return -1. - Examples: >>> jump_search([0, 1, 2, 3, 4, 5], 3) 3 >>> jump_search([-5, -2, -1], -1) 2 >>> jump_search([0, 5, 10, 20], 8) -1 - >>> jump_search([0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610], 55) - 10 >>> jump_search(["aa", "bb", "cc", "dd", "ee", "ff"], "ee") 4 """ - arr_size = len(arr) - block_size = int(math.sqrt(arr_size)) + if arr_size == 0: + return -1 + block_size = int(math.sqrt(arr_size)) prev = 0 step = block_size + + # Finding the block where element is present while arr[min(step, arr_size) - 1] < item: prev = step step += block_size if prev >= arr_size: return -1 + # Linear search within the block while arr[prev] < item: prev += 1 if prev == min(step, arr_size): return -1 + if arr[prev] == item: return prev return -1 - if __name__ == "__main__": - user_input = input("Enter numbers separated by a comma:\n").strip() - array = [int(item) for item in user_input.split(",")] - x = int(input("Enter the number to be searched:\n")) + import doctest - res = jump_search(array, x) - if res == -1: - print("Number not found!") - else: - print(f"Number {x} is at index {res}") + doctest.testmod() + user_input = input("Enter numbers separated by a comma:\n").strip() + if user_input: + array: list[Any] = [int(i) for i in user_input.split(",")] + search_item = int(input("Enter the number to be searched:\n")) + res = jump_search(array, search_item) + if res == -1: + print("Number not found!") + else: + print(f"Number {search_item} is at index {res}") \ No newline at end of file From 3c96273fc8707a533806e5cf0863988af78c36e1 Mon Sep 17 00:00:00 2001 From: tusharynayaka Date: Tue, 3 Mar 2026 18:43:24 +0530 Subject: [PATCH 08/16] fix: address ruff I001, UP047, and W292 in jump_search --- searches/jump_search.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/searches/jump_search.py b/searches/jump_search.py index e50cdd146762..7bf9184fd272 100644 --- a/searches/jump_search.py +++ b/searches/jump_search.py @@ -5,14 +5,12 @@ from __future__ import annotations import math -from typing import Any, Protocol, TypeVar +from typing import Any, Protocol class Comparable(Protocol): def __lt__(self, other: Any, /) -> bool: ... -T = TypeVar("T", bound=Comparable) - -def jump_search(arr: list[T], item: T) -> int: +def jump_search[T: Comparable](arr: list[T], item: T) -> int: """ Python implementation of the jump search algorithm. Return the index if the `item` is found, otherwise return -1. @@ -63,4 +61,5 @@ def jump_search(arr: list[T], item: T) -> int: if res == -1: print("Number not found!") else: - print(f"Number {search_item} is at index {res}") \ No newline at end of file + print(f"Number {search_item} is at index {res}") + \ No newline at end of file From d308b352dcf36277bbb39603360de86cbeaf34a1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 3 Mar 2026 13:15:49 +0000 Subject: [PATCH 09/16] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- searches/jump_search.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/searches/jump_search.py b/searches/jump_search.py index 7bf9184fd272..ea6f699f63f8 100644 --- a/searches/jump_search.py +++ b/searches/jump_search.py @@ -2,14 +2,17 @@ """ Pure Python implementation of the jump search algorithm. """ + from __future__ import annotations import math from typing import Any, Protocol + class Comparable(Protocol): def __lt__(self, other: Any, /) -> bool: ... + def jump_search[T: Comparable](arr: list[T], item: T) -> int: """ Python implementation of the jump search algorithm. @@ -49,6 +52,7 @@ def jump_search[T: Comparable](arr: list[T], item: T) -> int: return prev return -1 + if __name__ == "__main__": import doctest @@ -62,4 +66,3 @@ def jump_search[T: Comparable](arr: list[T], item: T) -> int: print("Number not found!") else: print(f"Number {search_item} is at index {res}") - \ No newline at end of file From b13b6e0a4b1965cba3698266f6dbf17432e64a3c Mon Sep 17 00:00:00 2001 From: tusharynayaka Date: Tue, 3 Mar 2026 18:45:45 +0530 Subject: [PATCH 10/16] fix: remove hidden whitespace and fix newline in jump_search --- searches/jump_search.py | 1 - 1 file changed, 1 deletion(-) diff --git a/searches/jump_search.py b/searches/jump_search.py index 29ea7d25d437..21c9668916ef 100644 --- a/searches/jump_search.py +++ b/searches/jump_search.py @@ -4,7 +4,6 @@ """ from __future__ import annotations - import math from collections.abc import Sequence from typing import Any, Protocol From 4235f83ccb7dd25a268f02061aa6189baa50beae Mon Sep 17 00:00:00 2001 From: tusharynayaka Date: Thu, 9 Apr 2026 06:37:16 +0530 Subject: [PATCH 11/16] Fix: implement first occurrence logic and restore doctests --- searches/binary_search.py | 104 +++++++++++++++----------------------- 1 file changed, 41 insertions(+), 63 deletions(-) diff --git a/searches/binary_search.py b/searches/binary_search.py index 8597f09b90bf..3e65b3bfc110 100644 --- a/searches/binary_search.py +++ b/searches/binary_search.py @@ -21,9 +21,6 @@ def bisect_left( Locates the first element in a sorted array that is larger or equal to a given value. - It has the same interface as - https://docs.python.org/3/library/bisect.html#bisect.bisect_left . - :param sorted_collection: some ascending sorted collection with comparable items :param item: item to bisect :param lo: lowest index to consider (as in sorted_collection[lo:hi]) @@ -62,16 +59,6 @@ def bisect_right( """ Locates the first element in a sorted array that is larger than a given value. - It has the same interface as - https://docs.python.org/3/library/bisect.html#bisect.bisect_right . - - :param sorted_collection: some ascending sorted collection with comparable items - :param item: item to bisect - :param lo: lowest index to consider (as in sorted_collection[lo:hi]) - :param hi: past the highest index to consider (as in sorted_collection[lo:hi]) - :return: index i such that all values in sorted_collection[lo:i] are <= item and - all values in sorted_collection[i:hi] are > item. - Examples: >>> bisect_right([0, 5, 7, 10, 15], 0) 1 @@ -79,10 +66,6 @@ def bisect_right( 5 >>> bisect_right([0, 5, 7, 10, 15], 6) 2 - >>> bisect_right([0, 5, 7, 10, 15], 15, 1, 3) - 3 - >>> bisect_right([0, 5, 7, 10, 15], 6, 2) - 2 """ if hi < 0: hi = len(sorted_collection) @@ -103,9 +86,6 @@ def insort_left( """ Inserts a given value into a sorted array before other values with the same value. - It has the same interface as - https://docs.python.org/3/library/bisect.html#bisect.insort_left . - Examples: >>> sorted_collection = [0, 5, 7, 10, 15] >>> insort_left(sorted_collection, 6) @@ -121,9 +101,6 @@ def insort_right( """ Inserts a given value into a sorted array after other values with the same value. - It has the same interface as - https://docs.python.org/3/library/bisect.html#bisect.insort_right . - Examples: >>> sorted_collection = [0, 5, 7, 10, 15] >>> insort_right(sorted_collection, 6) @@ -135,7 +112,7 @@ def insort_right( def binary_search(sorted_collection: list[int], item: int) -> int: """Pure implementation of a binary search algorithm in Python. - Finds the first occurrence of the item. + Updated to find the first occurrence of the item. :param sorted_collection: some ascending sorted collection with comparable items :param item: item value to search @@ -153,6 +130,7 @@ def binary_search(sorted_collection: list[int], item: int) -> int: """ if any(a > b for a, b in pairwise(sorted_collection)): raise ValueError("sorted_collection must be sorted in ascending order") + left = 0 right = len(sorted_collection) - 1 result = -1 @@ -170,9 +148,14 @@ def binary_search(sorted_collection: list[int], item: int) -> int: def binary_search_std_lib(sorted_collection: list[int], item: int) -> int: - """Implementation of a binary search algorithm using stdlib""" - if list(sorted_collection) != sorted(sorted_collection): - raise ValueError("sorted_collection must be sorted in ascending order") + """Binary search algorithm in Python using stdlib. + Finds the first occurrence. + + >>> binary_search_std_lib([0, 5, 7, 10, 15], 0) + 0 + >>> binary_search_std_lib([1, 2, 2, 2, 3], 2) + 1 + """ index = bisect.bisect_left(sorted_collection, item) if index != len(sorted_collection) and sorted_collection[index] == item: return index @@ -180,32 +163,16 @@ def binary_search_std_lib(sorted_collection: list[int], item: int) -> int: def binary_search_with_duplicates(sorted_collection: list[int], item: int) -> list[int]: - """Returns a list of all indexes where the target occurs.""" - if list(sorted_collection) != sorted(sorted_collection): - raise ValueError("sorted_collection must be sorted in ascending order") + """Returns a list of all indexes where the target occurs. - def lower_bound(sorted_collection: list[int], item: int) -> int: - left, right = 0, len(sorted_collection) - while left < right: - midpoint = left + (right - left) // 2 - if sorted_collection[midpoint] < item: - left = midpoint + 1 - else: - right = midpoint - return left - - def upper_bound(sorted_collection: list[int], item: int) -> int: - left, right = 0, len(sorted_collection) - while left < right: - midpoint = left + (right - left) // 2 - if sorted_collection[midpoint] <= item: - left = midpoint + 1 - else: - right = midpoint - return left - - left = lower_bound(sorted_collection, item) - right = upper_bound(sorted_collection, item) + Examples: + >>> binary_search_with_duplicates([1, 2, 2, 2, 3], 2) + [1, 2, 3] + >>> binary_search_with_duplicates([1, 2, 2, 2, 3], 4) + [] + """ + left = bisect_left(sorted_collection, item) + right = bisect_right(sorted_collection, item) if left == len(sorted_collection) or sorted_collection[left] != item: return [] @@ -215,20 +182,26 @@ def upper_bound(sorted_collection: list[int], item: int) -> int: def binary_search_by_recursion( sorted_collection: list[int], item: int, left: int = 0, right: int = -1 ) -> int: - """Recursive binary search finding the first occurrence.""" + """ + Recursive binary search finding the first occurrence. + """ if right < 0: right = len(sorted_collection) - 1 - if list(sorted_collection) != sorted(sorted_collection): - raise ValueError("sorted_collection must be sorted in ascending order") + + # Base case: range is empty if right < left: return -1 midpoint = left + (right - left) // 2 if sorted_collection[midpoint] == item: - # Check if there is an occurrence to the left - res = binary_search_by_recursion(sorted_collection, item, left, midpoint - 1) - return res if res != -1 else midpoint + # We found a match! Now see if there's an earlier one to the left. + # CRITICAL: Only recurse if there is actually space to the left + if midpoint > left: + res = binary_search_by_recursion(sorted_collection, item, left, midpoint - 1) + return res if res != -1 else midpoint + return midpoint + elif sorted_collection[midpoint] > item: return binary_search_by_recursion(sorted_collection, item, left, midpoint - 1) else: @@ -236,9 +209,14 @@ def binary_search_by_recursion( def exponential_search(sorted_collection: list[int], item: int) -> int: - """Implementation of an exponential search algorithm.""" - if list(sorted_collection) != sorted(sorted_collection): - raise ValueError("sorted_collection must be sorted in ascending order") + """Implementation of an exponential search algorithm finding the first occurrence. + + Examples: + >>> exponential_search([0, 5, 7, 10, 15], 0) + 0 + >>> exponential_search([1, 2, 2, 2, 3], 2) + 1 + """ if not sorted_collection: return -1 bound = 1 @@ -273,6 +251,6 @@ def exponential_search(sorted_collection: list[int], item: int) -> int: print( f"{name:>26}:", timeit.timeit( - f"{name}(collection, 500)", setup=setup, number=5_000, globals=globals() + f"{name}(list(collection), 500)", setup=setup, number=5_000, globals=globals() ), - ) + ) \ No newline at end of file From 5ede25708bb54e42707cb2a41c206d2ac3afaa4c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2026 01:12:04 +0000 Subject: [PATCH 12/16] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- searches/binary_search.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/searches/binary_search.py b/searches/binary_search.py index 3e65b3bfc110..7377f3b9194d 100644 --- a/searches/binary_search.py +++ b/searches/binary_search.py @@ -130,7 +130,7 @@ def binary_search(sorted_collection: list[int], item: int) -> int: """ if any(a > b for a, b in pairwise(sorted_collection)): raise ValueError("sorted_collection must be sorted in ascending order") - + left = 0 right = len(sorted_collection) - 1 result = -1 @@ -187,7 +187,7 @@ def binary_search_by_recursion( """ if right < 0: right = len(sorted_collection) - 1 - + # Base case: range is empty if right < left: return -1 @@ -198,10 +198,12 @@ def binary_search_by_recursion( # We found a match! Now see if there's an earlier one to the left. # CRITICAL: Only recurse if there is actually space to the left if midpoint > left: - res = binary_search_by_recursion(sorted_collection, item, left, midpoint - 1) + res = binary_search_by_recursion( + sorted_collection, item, left, midpoint - 1 + ) return res if res != -1 else midpoint return midpoint - + elif sorted_collection[midpoint] > item: return binary_search_by_recursion(sorted_collection, item, left, midpoint - 1) else: @@ -251,6 +253,9 @@ def exponential_search(sorted_collection: list[int], item: int) -> int: print( f"{name:>26}:", timeit.timeit( - f"{name}(list(collection), 500)", setup=setup, number=5_000, globals=globals() + f"{name}(list(collection), 500)", + setup=setup, + number=5_000, + globals=globals(), ), - ) \ No newline at end of file + ) From f858e21abf84108d85d0beb833e187d2b3a5c99a Mon Sep 17 00:00:00 2001 From: tusharynayaka Date: Thu, 9 Apr 2026 16:13:35 +0530 Subject: [PATCH 13/16] style: fix import sorting to satisfy ruff --- machine_learning/linear_discriminant_analysis.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/machine_learning/linear_discriminant_analysis.py b/machine_learning/linear_discriminant_analysis.py index de2d1de46ba1..c955fea15b18 100644 --- a/machine_learning/linear_discriminant_analysis.py +++ b/machine_learning/linear_discriminant_analysis.py @@ -247,8 +247,7 @@ def accuracy(actual_y: list, predicted_y: list) -> float: # of all data and multiplied by 100 return (correct / len(actual_y)) * 100 - -def valid_input[num]( +def valid_input( input_type: Callable[[object], num], # Usually float or int input_msg: str, err_msg: str, From 40090f0cb0de7eb090051de02062ba01fd81b250 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2026 10:44:39 +0000 Subject: [PATCH 14/16] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- machine_learning/linear_discriminant_analysis.py | 1 + 1 file changed, 1 insertion(+) diff --git a/machine_learning/linear_discriminant_analysis.py b/machine_learning/linear_discriminant_analysis.py index c955fea15b18..5137de3cc1c2 100644 --- a/machine_learning/linear_discriminant_analysis.py +++ b/machine_learning/linear_discriminant_analysis.py @@ -247,6 +247,7 @@ def accuracy(actual_y: list, predicted_y: list) -> float: # of all data and multiplied by 100 return (correct / len(actual_y)) * 100 + def valid_input( input_type: Callable[[object], num], # Usually float or int input_msg: str, From f059c6da3efbe229d89a8d3b07ab65124db6771d Mon Sep 17 00:00:00 2001 From: tusharynayaka Date: Thu, 9 Apr 2026 16:27:47 +0530 Subject: [PATCH 15/16] final: restore original doctests and fix logic inconsistencies --- .../linear_discriminant_analysis.py | 3 +- searches/binary_search.py | 92 ++++++++++--------- searches/jump_search.py | 1 + 3 files changed, 53 insertions(+), 43 deletions(-) diff --git a/machine_learning/linear_discriminant_analysis.py b/machine_learning/linear_discriminant_analysis.py index 5137de3cc1c2..68ec6c7e10ce 100644 --- a/machine_learning/linear_discriminant_analysis.py +++ b/machine_learning/linear_discriminant_analysis.py @@ -247,8 +247,7 @@ def accuracy(actual_y: list, predicted_y: list) -> float: # of all data and multiplied by 100 return (correct / len(actual_y)) * 100 - -def valid_input( +def valid_input[num]( input_type: Callable[[object], num], # Usually float or int input_msg: str, err_msg: str, diff --git a/searches/binary_search.py b/searches/binary_search.py index 7377f3b9194d..3b77ec4ed9df 100644 --- a/searches/binary_search.py +++ b/searches/binary_search.py @@ -66,6 +66,10 @@ def bisect_right( 5 >>> bisect_right([0, 5, 7, 10, 15], 6) 2 + >>> bisect_right([0, 5, 7, 10, 15], 15, 1, 3) + 3 + >>> bisect_right([0, 5, 7, 10, 15], 6, 2) + 2 """ if hi < 0: hi = len(sorted_collection) @@ -91,6 +95,14 @@ def insort_left( >>> insort_left(sorted_collection, 6) >>> sorted_collection [0, 5, 6, 7, 10, 15] + >>> sorted_collection = [0, 5, 7, 10, 15] + >>> insort_left(sorted_collection, 20) + >>> sorted_collection + [0, 5, 7, 10, 15, 20] + >>> sorted_collection = [0, 5, 7, 10, 15] + >>> insort_left(sorted_collection, 15, 1, 3) + >>> sorted_collection + [0, 5, 7, 15, 10, 15] """ sorted_collection.insert(bisect_left(sorted_collection, item, lo, hi), item) @@ -106,24 +118,27 @@ def insort_right( >>> insort_right(sorted_collection, 6) >>> sorted_collection [0, 5, 6, 7, 10, 15] + >>> sorted_collection = [0, 5, 7, 10, 15] + >>> insort_right(sorted_collection, 20) + >>> sorted_collection + [0, 5, 7, 10, 15, 20] + >>> sorted_collection = [0, 5, 7, 10, 15] + >>> insort_right(sorted_collection, 15, 1, 3) + >>> sorted_collection + [0, 5, 7, 15, 10, 15] """ sorted_collection.insert(bisect_right(sorted_collection, item, lo, hi), item) def binary_search(sorted_collection: list[int], item: int) -> int: """Pure implementation of a binary search algorithm in Python. - Updated to find the first occurrence of the item. - - :param sorted_collection: some ascending sorted collection with comparable items - :param item: item value to search - :return: index of the found item or -1 if the item is not found Examples: >>> binary_search([0, 5, 7, 10, 15], 0) 0 >>> binary_search([0, 5, 7, 10, 15], 15) 4 - >>> binary_search([1, 2, 2, 2, 3], 2) + >>> binary_search([0, 5, 7, 10, 15], 5) 1 >>> binary_search([0, 5, 7, 10, 15], 6) -1 @@ -139,7 +154,7 @@ def binary_search(sorted_collection: list[int], item: int) -> int: midpoint = left + (right - left) // 2 if sorted_collection[midpoint] == item: result = midpoint - right = midpoint - 1 # Continue searching left + right = midpoint - 1 # Logic for first occurrence elif item < sorted_collection[midpoint]: right = midpoint - 1 else: @@ -148,13 +163,16 @@ def binary_search(sorted_collection: list[int], item: int) -> int: def binary_search_std_lib(sorted_collection: list[int], item: int) -> int: - """Binary search algorithm in Python using stdlib. - Finds the first occurrence. + """Binary search using stdlib. >>> binary_search_std_lib([0, 5, 7, 10, 15], 0) 0 - >>> binary_search_std_lib([1, 2, 2, 2, 3], 2) + >>> binary_search_std_lib([0, 5, 7, 10, 15], 15) + 4 + >>> binary_search_std_lib([0, 5, 7, 10, 15], 5) 1 + >>> binary_search_std_lib([0, 5, 7, 10, 15], 6) + -1 """ index = bisect.bisect_left(sorted_collection, item) if index != len(sorted_collection) and sorted_collection[index] == item: @@ -166,6 +184,10 @@ def binary_search_with_duplicates(sorted_collection: list[int], item: int) -> li """Returns a list of all indexes where the target occurs. Examples: + >>> binary_search_with_duplicates([0, 5, 7, 10, 15], 0) + [0] + >>> binary_search_with_duplicates([0, 5, 7, 10, 15], 15) + [4] >>> binary_search_with_duplicates([1, 2, 2, 2, 3], 2) [1, 2, 3] >>> binary_search_with_duplicates([1, 2, 2, 2, 3], 4) @@ -182,28 +204,31 @@ def binary_search_with_duplicates(sorted_collection: list[int], item: int) -> li def binary_search_by_recursion( sorted_collection: list[int], item: int, left: int = 0, right: int = -1 ) -> int: - """ - Recursive binary search finding the first occurrence. + """Recursive binary search. + + Examples: + >>> binary_search_by_recursion([0, 5, 7, 10, 15], 0, 0, 4) + 0 + >>> binary_search_by_recursion([0, 5, 7, 10, 15], 15, 0, 4) + 4 + >>> binary_search_by_recursion([0, 5, 7, 10, 15], 5, 0, 4) + 1 + >>> binary_search_by_recursion([0, 5, 7, 10, 15], 6, 0, 4) + -1 """ if right < 0: right = len(sorted_collection) - 1 - # Base case: range is empty if right < left: return -1 midpoint = left + (right - left) // 2 if sorted_collection[midpoint] == item: - # We found a match! Now see if there's an earlier one to the left. - # CRITICAL: Only recurse if there is actually space to the left if midpoint > left: - res = binary_search_by_recursion( - sorted_collection, item, left, midpoint - 1 - ) + res = binary_search_by_recursion(sorted_collection, item, left, midpoint - 1) return res if res != -1 else midpoint return midpoint - elif sorted_collection[midpoint] > item: return binary_search_by_recursion(sorted_collection, item, left, midpoint - 1) else: @@ -211,13 +236,17 @@ def binary_search_by_recursion( def exponential_search(sorted_collection: list[int], item: int) -> int: - """Implementation of an exponential search algorithm finding the first occurrence. + """Exponential search algorithm. Examples: >>> exponential_search([0, 5, 7, 10, 15], 0) 0 - >>> exponential_search([1, 2, 2, 2, 3], 2) + >>> exponential_search([0, 5, 7, 10, 15], 15) + 4 + >>> exponential_search([0, 5, 7, 10, 15], 5) 1 + >>> exponential_search([0, 5, 7, 10, 15], 6) + -1 """ if not sorted_collection: return -1 @@ -239,23 +268,4 @@ def exponential_search(sorted_collection: list[int], item: int) -> int: if __name__ == "__main__": import doctest - import timeit - - doctest.testmod() - for search in searches: - name = f"{search.__name__:>26}" - print(f"{name}: {search([0, 5, 7, 10, 15], 10) = }") - - print("\nBenchmarks...") - setup = "collection = range(1000)" - for search in searches: - name = search.__name__ - print( - f"{name:>26}:", - timeit.timeit( - f"{name}(list(collection), 500)", - setup=setup, - number=5_000, - globals=globals(), - ), - ) + doctest.testmod() \ No newline at end of file diff --git a/searches/jump_search.py b/searches/jump_search.py index 21c9668916ef..29ea7d25d437 100644 --- a/searches/jump_search.py +++ b/searches/jump_search.py @@ -4,6 +4,7 @@ """ from __future__ import annotations + import math from collections.abc import Sequence from typing import Any, Protocol From 5a9e860764fd6dbd9d8ecc983363880b25f4340d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2026 10:58:52 +0000 Subject: [PATCH 16/16] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- machine_learning/linear_discriminant_analysis.py | 1 + searches/binary_search.py | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/machine_learning/linear_discriminant_analysis.py b/machine_learning/linear_discriminant_analysis.py index 68ec6c7e10ce..de2d1de46ba1 100644 --- a/machine_learning/linear_discriminant_analysis.py +++ b/machine_learning/linear_discriminant_analysis.py @@ -247,6 +247,7 @@ def accuracy(actual_y: list, predicted_y: list) -> float: # of all data and multiplied by 100 return (correct / len(actual_y)) * 100 + def valid_input[num]( input_type: Callable[[object], num], # Usually float or int input_msg: str, diff --git a/searches/binary_search.py b/searches/binary_search.py index 3b77ec4ed9df..edc30aa8257a 100644 --- a/searches/binary_search.py +++ b/searches/binary_search.py @@ -226,7 +226,9 @@ def binary_search_by_recursion( if sorted_collection[midpoint] == item: if midpoint > left: - res = binary_search_by_recursion(sorted_collection, item, left, midpoint - 1) + res = binary_search_by_recursion( + sorted_collection, item, left, midpoint - 1 + ) return res if res != -1 else midpoint return midpoint elif sorted_collection[midpoint] > item: @@ -268,4 +270,5 @@ def exponential_search(sorted_collection: list[int], item: int) -> int: if __name__ == "__main__": import doctest - doctest.testmod() \ No newline at end of file + + doctest.testmod()