From dda99e6c9f753f3c45072d05b35d691e042cd8db Mon Sep 17 00:00:00 2001 From: Jesun Ahmad Ushno Date: Tue, 17 Feb 2026 05:13:12 -0500 Subject: [PATCH 1/2] Fix typing and lint issues --- .../hashing/hash_table_with_linked_list.py | 2 +- graphs/check_bipatrite.py | 29 +++++++++---------- .../linear_discriminant_analysis.py | 12 +++----- searches/jump_search.py | 7 ++--- 4 files changed, 20 insertions(+), 30 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/graphs/check_bipatrite.py b/graphs/check_bipatrite.py index 897c78850d58..ba988f788388 100644 --- a/graphs/check_bipatrite.py +++ b/graphs/check_bipatrite.py @@ -1,18 +1,20 @@ from collections import defaultdict, deque +from collections.abc import Hashable, Iterable, Mapping -def is_bipartite_dfs(graph: dict[int, list[int]]) -> bool: +def is_bipartite_dfs(graph: Mapping[Hashable, Iterable[Hashable]]) -> bool: """ Check if a graph is bipartite using depth-first search (DFS). Args: - `graph`: Adjacency list representing the graph. + `graph`: Mapping of nodes to their neighbors. Nodes must be hashable. Returns: ``True`` if bipartite, ``False`` otherwise. Checks if the graph can be divided into two sets of vertices, such that no two - vertices within the same set are connected by an edge. + vertices within the same set are connected by an edge. Neighbor nodes that do + not appear as keys are treated as isolated nodes with no outgoing edges. Examples: @@ -33,7 +35,6 @@ def is_bipartite_dfs(graph: dict[int, list[int]]) -> bool: >>> is_bipartite_dfs({7: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2], 4: [0]}) False - >>> # FIXME: This test should fails with KeyError: 4. >>> is_bipartite_dfs({0: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2], 9: [0]}) False >>> is_bipartite_dfs({0: [-1, 3], 1: [0, -2]}) @@ -43,8 +44,6 @@ def is_bipartite_dfs(graph: dict[int, list[int]]) -> bool: >>> is_bipartite_dfs({0.9: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2]}) True - >>> # FIXME: This test should fails with - >>> # TypeError: list indices must be integers or... >>> is_bipartite_dfs({0: [1.0, 3.0], 1.0: [0, 2.0], 2.0: [1.0, 3.0], 3.0: [0, 2.0]}) True >>> is_bipartite_dfs({"a": [1, 3], "b": [0, 2], "c": [1, 3], "d": [0, 2]}) @@ -53,7 +52,7 @@ def is_bipartite_dfs(graph: dict[int, list[int]]) -> bool: True """ - def depth_first_search(node: int, color: int) -> bool: + def depth_first_search(node: Hashable, color: int) -> bool: """ Perform Depth-First Search (DFS) on the graph starting from a node. @@ -74,25 +73,26 @@ def depth_first_search(node: int, color: int) -> bool: return False return visited[node] == color - visited: defaultdict[int, int] = defaultdict(lambda: -1) + visited: defaultdict[Hashable, int] = defaultdict(lambda: -1) for node in graph: if visited[node] == -1 and not depth_first_search(node, 0): return False return True -def is_bipartite_bfs(graph: dict[int, list[int]]) -> bool: +def is_bipartite_bfs(graph: Mapping[Hashable, Iterable[Hashable]]) -> bool: """ Check if a graph is bipartite using a breadth-first search (BFS). Args: - `graph`: Adjacency list representing the graph. + `graph`: Mapping of nodes to their neighbors. Nodes must be hashable. Returns: ``True`` if bipartite, ``False`` otherwise. Check if the graph can be divided into two sets of vertices, such that no two - vertices within the same set are connected by an edge. + vertices within the same set are connected by an edge. Neighbor nodes that do + not appear as keys are treated as isolated nodes with no outgoing edges. Examples: @@ -113,7 +113,6 @@ def is_bipartite_bfs(graph: dict[int, list[int]]) -> bool: >>> is_bipartite_bfs({7: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2], 4: [0]}) False - >>> # FIXME: This test should fails with KeyError: 4. >>> is_bipartite_bfs({0: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2], 9: [0]}) False >>> is_bipartite_bfs({0: [-1, 3], 1: [0, -2]}) @@ -123,8 +122,6 @@ def is_bipartite_bfs(graph: dict[int, list[int]]) -> bool: >>> is_bipartite_bfs({0.9: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2]}) True - >>> # FIXME: This test should fails with - >>> # TypeError: list indices must be integers or... >>> is_bipartite_bfs({0: [1.0, 3.0], 1.0: [0, 2.0], 2.0: [1.0, 3.0], 3.0: [0, 2.0]}) True >>> is_bipartite_bfs({"a": [1, 3], "b": [0, 2], "c": [1, 3], "d": [0, 2]}) @@ -132,10 +129,10 @@ def is_bipartite_bfs(graph: dict[int, list[int]]) -> bool: >>> is_bipartite_bfs({0: ["b", "d"], 1: ["a", "c"], 2: ["b", "d"], 3: ["a", "c"]}) True """ - visited: defaultdict[int, int] = defaultdict(lambda: -1) + visited: defaultdict[Hashable, int] = defaultdict(lambda: -1) for node in graph: if visited[node] == -1: - queue: deque[int] = deque() + queue: deque[Hashable] = deque() queue.append(node) visited[node] = 0 while queue: diff --git a/machine_learning/linear_discriminant_analysis.py b/machine_learning/linear_discriminant_analysis.py index 8528ccbbae51..b99bffc35d8b 100644 --- a/machine_learning/linear_discriminant_analysis.py +++ b/machine_learning/linear_discriminant_analysis.py @@ -47,7 +47,6 @@ from math import log from os import name, system from random import gauss, seed -from typing import TypeVar # Make a training dataset drawn from a gaussian distribution @@ -249,16 +248,13 @@ def accuracy(actual_y: list, predicted_y: list) -> float: return (correct / len(actual_y)) * 100 -num = TypeVar("num") - - -def valid_input( - input_type: Callable[[object], num], # Usually float or int +def valid_input[T]( + input_type: Callable[[object], T], # Usually float or int input_msg: str, err_msg: str, - condition: Callable[[num], bool] = lambda _: True, + condition: Callable[[T], bool] = lambda _: True, default: str | None = None, -) -> num: +) -> T: """ Ask for user value and validate that it fulfill a condition. diff --git a/searches/jump_search.py b/searches/jump_search.py index e72d85e8a868..437faf306bb2 100644 --- a/searches/jump_search.py +++ b/searches/jump_search.py @@ -10,17 +10,14 @@ import math from collections.abc import Sequence -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: Sequence[T], item: T) -> int: +def jump_search[T: Comparable](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 c3fdea4f4fbe0a0bafe0a97b1aaf4512146bc32d Mon Sep 17 00:00:00 2001 From: Jesun Ahmad Ushno Date: Tue, 17 Feb 2026 06:05:20 -0500 Subject: [PATCH 2/2] Enhance linear_search and rec_linear_search with comprehensive doctests --- searches/linear_search.py | 77 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 2 deletions(-) diff --git a/searches/linear_search.py b/searches/linear_search.py index ba6e81d6bae4..a8f91c8ffc0c 100644 --- a/searches/linear_search.py +++ b/searches/linear_search.py @@ -12,6 +12,10 @@ def linear_search(sequence: list, target: int) -> int: """A pure Python implementation of a linear search algorithm + Linear search iterates through a collection sequentially until it finds the + target element or reaches the end of the collection. It works on both sorted + and unsorted collections. + :param sequence: a collection with comparable items (as sorted items not required in Linear Search) :param target: item value to search @@ -26,6 +30,42 @@ def linear_search(sequence: list, target: int) -> int: 1 >>> linear_search([0, 5, 7, 10, 15], 6) -1 + + >>> linear_search([], 5) + -1 + + >>> linear_search([42], 42) + 0 + + >>> linear_search([42], 99) + -1 + + >>> linear_search([3, 1, 4, 1, 5, 9, 2, 6], 1) + 1 + + >>> linear_search([3, 1, 4, 1, 5, 9, 2, 6], 9) + 5 + + >>> linear_search([10, 20, 30, 40, 50], 30) + 2 + + >>> linear_search([5, 5, 5, 5], 5) + 0 + + >>> linear_search([-5, -2, 0, 3, 7], -5) + 0 + + >>> linear_search([-5, -2, 0, 3, 7], 0) + 2 + + >>> linear_search([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 10) + 9 + + >>> linear_search([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 1) + 0 + + >>> linear_search([100, 200, 300, 400], 250) + -1 """ for index, item in enumerate(sequence): if item == target: @@ -37,10 +77,13 @@ def rec_linear_search(sequence: list, low: int, high: int, target: int) -> int: """ A pure Python implementation of a recursive linear search algorithm + This is the recursive variant of linear search. It searches from both bounds + (low and high) converging toward the middle, checking both ends simultaneously. + :param sequence: a collection with comparable items (as sorted items not required in Linear Search) - :param low: Lower bound of the array - :param high: Higher bound of the array + :param low: Lower bound of the array (starting index) + :param high: Higher bound of the array (ending index) :param target: The element to be found :return: Index of the key or -1 if key not found @@ -53,6 +96,36 @@ def rec_linear_search(sequence: list, low: int, high: int, target: int) -> int: 1 >>> rec_linear_search([0, 30, 500, 100, 700], 0, 4, -6) -1 + + >>> rec_linear_search([42], 0, 0, 42) + 0 + + >>> rec_linear_search([42], 0, 0, 99) + -1 + + >>> rec_linear_search([1, 2, 3, 4, 5], 0, 4, 1) + 0 + + >>> rec_linear_search([1, 2, 3, 4, 5], 0, 4, 5) + 4 + + >>> rec_linear_search([1, 2, 3, 4, 5], 0, 4, 3) + 2 + + >>> rec_linear_search([10, 20, 30, 40, 50, 60], 0, 5, 40) + 3 + + >>> rec_linear_search([-5, -2, 0, 3, 7], 0, 4, -5) + 0 + + >>> rec_linear_search([-5, -2, 0, 3, 7], 0, 4, 7) + 4 + + >>> rec_linear_search([5, 5, 5, 5, 5], 0, 4, 5) + 0 + + >>> rec_linear_search([1, 2, 3, 4, 5], 0, 4, 0) + -1 """ if not (0 <= high < len(sequence) and 0 <= low < len(sequence)): raise Exception("Invalid upper or lower bound!")