Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 45 additions & 13 deletions search.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,21 @@
import math

class NotFoundError(Exception):
pass
def __init__(self, message="Element not found"):
self.message = message

def __repr__(self):
return f"NotFoundError('{self.message}')"

def __eq__(self, other):
"""Allows test comparisons with other NotFoundError objects."""
if isinstance(other, NotFoundError):
return self.message == other.message
return False

def __bool__(self):
"""Allows checking if the result is NOT NotFoundError."""
return False


class Search:
Expand All @@ -26,21 +40,39 @@ def linear(data: List[Any], target: Any, comparator: Callable[[Any, Any], bool])
Performs a linear search through the list.
Returns the index of the target, or -1 if not found.
"""
pass


if not data:
raise NotFoundError("List is empty.")

for index, item in enumerate(data):

if comparator(item, target):
return index

raise NotFoundError(f"Target '{target}' not found in the list.")
# =====================
# BINARY SEARCH
# =====================
@staticmethod
def binary(data: List[Any], target: Any, comparator: Callable[[Any, Any], int]) -> Any:
"""
Performs binary search on a sorted list.

The comparator should return:
- 0 if a == b
- negative if a < b
- positive if a > b
Returns the index of the found element, or -1 if not found.
"""
pass

if not data:
raise NotFoundError("List is empty.")

left, right = 0, len(data) - 1

while left <= right:
# Calculate mid point
mid = (left + right) // 2
comparison_result = comparator(data[mid], target)

if comparison_result == 0:
return mid

elif comparison_result < 0:
left = mid + 1

else:
right = mid - 1

raise NotFoundError(f"Target '{target}' not found in the list.")
114 changes: 61 additions & 53 deletions sort.py
Original file line number Diff line number Diff line change
@@ -1,72 +1,80 @@
from typing import List, Callable, Any

class Sorter:
"""
A sorting utility class providing multiple sorting algorithms.

Each method is static and takes:
- data: a list of elements to sort
- comparator: a function that compares two elements
and returns True if the first should come before the second.
"""
@staticmethod
def _merge_halves(left: List[Any], right: List[Any], comparator: Callable[[Any, Any], bool]) -> List[Any]:
result = []
i = 0
j = 0
while i < len(left) and j < len(right):
if comparator(left[i], right[j]):
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
result.extend(left[i:])
result.extend(right[j:])
return result

@staticmethod
def merge(data: List[Any], comparator: Callable[[Any, Any], bool]) -> List[Any]:
"""
Sorts the list using the merge sort algorithm.
if len(data) <= 1:
return data

Args:
data (List[Any]): The list to sort.
comparator (Callable[[Any, Any], bool]): Comparison function.

Returns:
List[Any]: A new sorted list.
"""
pass
mid = len(data) // 2
left_half = Sorter.merge(data[:mid], comparator)
right_half = Sorter.merge(data[mid:], comparator)

return Sorter._merge_halves(left_half, right_half, comparator)


@staticmethod
def insertion(data: List[Any], comparator: Callable[[Any, Any], bool]) -> List[Any]:
"""
Sorts the list using the insertion sort algorithm.

Args:
data (List[Any]): The list to sort.
comparator (Callable[[Any, Any], bool]): Comparison function.

Returns:
List[Any]: A new sorted list.
"""
pass
sorted_data = list(data)
n = len(sorted_data)

for i in range(1, n):
key = sorted_data[i]
j = i - 1

# Shift elements that should come AFTER the key according to comparator
while j >= 0 and comparator(key, sorted_data[j]):
sorted_data[j + 1] = sorted_data[j]
j -= 1

sorted_data[j + 1] = key

return sorted_data


@staticmethod
def bubble(data: List[Any], comparator: Callable[[Any, Any], bool]) -> List[Any]:
"""
Sorts the list using the bubble sort algorithm.
sorted_data = list(data)
n = len(sorted_data)

Args:
data (List[Any]): The list to sort.
comparator (Callable[[Any, Any], bool]): Comparison function.
for i in range(n - 1):
swapped = False
for j in range(n - 1 - i):
# If the element at j should NOT come before the element at j+1, swap them.
if not comparator(sorted_data[j], sorted_data[j+1]):
sorted_data[j], sorted_data[j+1] = sorted_data[j+1], sorted_data[j]
swapped = True

Returns:
List[Any]: A new sorted list.
"""
pass
if not swapped:
break
return sorted_data

@staticmethod
def sort(data: List[Any], comparator: Callable[[Any, Any], bool], method: str = "merge") -> List[Any]:
"""
Sorts the list using the specified algorithm.

Args:
data (List[Any]): The list to sort.
comparator (Callable[[Any, Any], bool]): Comparison function.
method (str): Sorting algorithm ('merge', 'insertion', or 'bubble').

Returns:
List[Any]: A new sorted list.

Raises:
ValueError: If an unknown sort method is provided.
"""
pass
method_lower = method.lower() # FIX: Handle case-insensitivity

if method_lower == "merge":
return Sorter.merge(data, comparator)
elif method_lower == "insertion":
return Sorter.insertion(data, comparator)
elif method_lower == "bubble":
return Sorter.bubble(data, comparator)
else:
raise ValueError(f"Unknown sort method: {method}. Must be 'merge', 'insertion', or 'bubble'.")