Skip to content
Closed
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
141 changes: 21 additions & 120 deletions sorts/bubble_sort.py
Original file line number Diff line number Diff line change
@@ -1,138 +1,39 @@
from typing import Any


def bubble_sort_iterative(collection: list[Any]) -> list[Any]:
"""Pure implementation of bubble sort algorithm in Python
def bubble_sort(collection: list[int]) -> list[int]:
"""
Sorts a list in ascending order using the bubble sort algorithm.

:param collection: some mutable ordered collection with heterogeneous
comparable items inside
:return: the same collection ordered in ascending order
:param collection: A list of integers to be sorted.
:return: The sorted list.

Examples:
>>> bubble_sort_iterative([0, 5, 2, 3, 2])
>>> bubble_sort([0, 5, 3, 2, 2])
[0, 2, 2, 3, 5]
>>> bubble_sort_iterative([])

>>> bubble_sort([])
[]
>>> bubble_sort_iterative([-2, -45, -5])

>>> bubble_sort([-2, -5, -45])
[-45, -5, -2]
>>> bubble_sort_iterative([-23, 0, 6, -4, 34])
[-23, -4, 0, 6, 34]
>>> bubble_sort_iterative([1, 2, 3, 4])
[1, 2, 3, 4]
>>> bubble_sort_iterative([3, 3, 3, 3])
[3, 3, 3, 3]
>>> bubble_sort_iterative([56])
[56]
>>> bubble_sort_iterative([0, 5, 2, 3, 2]) == sorted([0, 5, 2, 3, 2])
True
>>> bubble_sort_iterative([]) == sorted([])
True
>>> bubble_sort_iterative([-2, -45, -5]) == sorted([-2, -45, -5])
True
>>> bubble_sort_iterative([-23, 0, 6, -4, 34]) == sorted([-23, 0, 6, -4, 34])
True
>>> bubble_sort_iterative(['d', 'a', 'b', 'e']) == sorted(['d', 'a', 'b', 'e'])
True
>>> bubble_sort_iterative(['z', 'a', 'y', 'b', 'x', 'c'])
['a', 'b', 'c', 'x', 'y', 'z']
>>> bubble_sort_iterative([1.1, 3.3, 5.5, 7.7, 2.2, 4.4, 6.6])
[1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7]
>>> bubble_sort_iterative([1, 3.3, 5, 7.7, 2, 4.4, 6])
[1, 2, 3.3, 4.4, 5, 6, 7.7]
>>> import random
>>> collection_arg = random.sample(range(-50, 50), 100)
>>> bubble_sort_iterative(collection_arg) == sorted(collection_arg)
True
>>> import string
>>> collection_arg = random.choices(string.ascii_letters + string.digits, k=100)
>>> bubble_sort_iterative(collection_arg) == sorted(collection_arg)
True
"""
length = len(collection)
for i in reversed(range(length)):
for i in range(length):
swapped = False
for j in range(i):
for j in range(length - 1 - i):
if collection[j] > collection[j + 1]:
swapped = True
collection[j], collection[j + 1] = collection[j + 1], collection[j]
swapped = True
if not swapped:
break # Stop iteration if the collection is sorted.
break
return collection


def bubble_sort_recursive(collection: list[Any]) -> list[Any]:
"""It is similar iterative bubble sort but recursive.

:param collection: mutable ordered sequence of elements
:return: the same list in ascending order

Examples:
>>> bubble_sort_recursive([0, 5, 2, 3, 2])
[0, 2, 2, 3, 5]
>>> bubble_sort_recursive([])
[]
>>> bubble_sort_recursive([-2, -45, -5])
[-45, -5, -2]
>>> bubble_sort_recursive([-23, 0, 6, -4, 34])
[-23, -4, 0, 6, 34]
>>> bubble_sort_recursive([0, 5, 2, 3, 2]) == sorted([0, 5, 2, 3, 2])
True
>>> bubble_sort_recursive([]) == sorted([])
True
>>> bubble_sort_recursive([-2, -45, -5]) == sorted([-2, -45, -5])
True
>>> bubble_sort_recursive([-23, 0, 6, -4, 34]) == sorted([-23, 0, 6, -4, 34])
True
>>> bubble_sort_recursive(['d', 'a', 'b', 'e']) == sorted(['d', 'a', 'b', 'e'])
True
>>> bubble_sort_recursive(['z', 'a', 'y', 'b', 'x', 'c'])
['a', 'b', 'c', 'x', 'y', 'z']
>>> bubble_sort_recursive([1.1, 3.3, 5.5, 7.7, 2.2, 4.4, 6.6])
[1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7]
>>> bubble_sort_recursive([1, 3.3, 5, 7.7, 2, 4.4, 6])
[1, 2, 3.3, 4.4, 5, 6, 7.7]
>>> bubble_sort_recursive(['a', 'Z', 'B', 'C', 'A', 'c'])
['A', 'B', 'C', 'Z', 'a', 'c']
>>> import random
>>> collection_arg = random.sample(range(-50, 50), 100)
>>> bubble_sort_recursive(collection_arg) == sorted(collection_arg)
True
>>> import string
>>> collection_arg = random.choices(string.ascii_letters + string.digits, k=100)
>>> bubble_sort_recursive(collection_arg) == sorted(collection_arg)
True
"""
length = len(collection)
swapped = False
for i in range(length - 1):
if collection[i] > collection[i + 1]:
collection[i], collection[i + 1] = collection[i + 1], collection[i]
swapped = True

return collection if not swapped else bubble_sort_recursive(collection)
def get_user_input() -> list[int]:
"""Helper function to get list from user input."""
user_input = input("Enter numbers separated by a comma:\n").strip()
return [int(item) for item in user_input.split(",")]


if __name__ == "__main__":
import doctest
from random import sample
from timeit import timeit

doctest.testmod()

# Benchmark: Iterative seems slightly faster than recursive.
num_runs = 10_000
unsorted = sample(range(-50, 50), 100)
timer_iterative = timeit(
"bubble_sort_iterative(unsorted[:])", globals=globals(), number=num_runs
)
print("\nIterative bubble sort:")
print(*bubble_sort_iterative(unsorted), sep=",")
print(f"Processing time (iterative): {timer_iterative:.5f}s for {num_runs:,} runs")

unsorted = sample(range(-50, 50), 100)
timer_recursive = timeit(
"bubble_sort_recursive(unsorted[:])", globals=globals(), number=num_runs
)
print("\nRecursive bubble sort:")
print(*bubble_sort_recursive(unsorted), sep=",")
print(f"Processing time (recursive): {timer_recursive:.5f}s for {num_runs:,} runs")
unsorted = get_user_input()
sorted_list = bubble_sort(unsorted)
print("Sorted List:", sorted_list)
86 changes: 30 additions & 56 deletions sorts/insertion_sort.py
Original file line number Diff line number Diff line change
@@ -1,69 +1,43 @@
"""
A pure Python implementation of the insertion sort algorithm

This algorithm sorts a collection by comparing adjacent elements.
When it finds that order is not respected, it moves the element compared
backward until the order is correct. It then goes back directly to the
element's initial position resuming forward comparison.

For doctests run following command:
python3 -m doctest -v insertion_sort.py

For manual testing run:
python3 insertion_sort.py
"""

from collections.abc import MutableSequence
from typing import Any, Protocol, TypeVar

def insertion_sort(collection: list[int]) -> list[int]:
"""
Sorts a list in ascending order using the insertion sort algorithm.

class Comparable(Protocol):
def __lt__(self, other: Any, /) -> bool: ...
:param collection: A list of integers to be sorted.
:return: The sorted list.

Examples:
>>> insertion_sort([0, 5, 3, 2, 2])
[0, 2, 2, 3, 5]

T = TypeVar("T", bound=Comparable)
>>> insertion_sort([])
[]

>>> insertion_sort([-2, -5, -45])
[-45, -5, -2]
"""
START_INDEX = 1 # Magic number replaced with constant

Check failure on line 18 in sorts/insertion_sort.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (N806)

sorts/insertion_sort.py:18:5: N806 Variable `START_INDEX` in function should be lowercase
length = len(collection)

def insertion_sort[T: Comparable](collection: MutableSequence[T]) -> MutableSequence[T]:
"""A pure Python implementation of the insertion sort algorithm
for insert_index in range(START_INDEX, length):
current_value = collection[insert_index]
position = insert_index

:param collection: some mutable ordered collection with heterogeneous
comparable items inside
:return: the same collection ordered by ascending
while position > 0 and collection[position - 1] > current_value:
collection[position] = collection[position - 1]
position -= 1

Examples:
>>> insertion_sort([0, 5, 3, 2, 2])
[0, 2, 2, 3, 5]
>>> insertion_sort([]) == sorted([])
True
>>> insertion_sort([-2, -5, -45]) == sorted([-2, -5, -45])
True
>>> insertion_sort(['d', 'a', 'b', 'e', 'c']) == sorted(['d', 'a', 'b', 'e', 'c'])
True
>>> import random
>>> collection = random.sample(range(-50, 50), 100)
>>> insertion_sort(collection) == sorted(collection)
True
>>> import string
>>> collection = random.choices(string.ascii_letters + string.digits, k=100)
>>> insertion_sort(collection) == sorted(collection)
True
"""
collection[position] = current_value

for insert_index in range(1, len(collection)):
insert_value = collection[insert_index]
while insert_index > 0 and insert_value < collection[insert_index - 1]:
collection[insert_index] = collection[insert_index - 1]
insert_index -= 1
collection[insert_index] = insert_value
return collection


if __name__ == "__main__":
from doctest import testmod
def get_user_input() -> list[int]:
"""Helper function to get list from user input."""
user_input = input("Enter numbers separated by a comma:\n").strip()
return [int(item) for item in user_input.split(",")]

testmod()

user_input = input("Enter numbers separated by a comma:\n").strip()
unsorted = [int(item) for item in user_input.split(",")]
print(f"{insertion_sort(unsorted) = }")
if __name__ == "__main__":
unsorted = get_user_input()
sorted_list = insertion_sort(unsorted)
print("Sorted List:", sorted_list)
80 changes: 33 additions & 47 deletions sorts/merge_sort.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,6 @@
"""
This is a pure Python implementation of the merge sort algorithm.

For doctests run following command:
python -m doctest -v merge_sort.py
or
python3 -m doctest -v merge_sort.py
For manual testing run:
python merge_sort.py
"""


def merge_sort(collection: list) -> list:
def merge_sort(collection: list[int]) -> list[int]:
"""
Sorts a list using the merge sort algorithm.

:param collection: A mutable ordered collection with comparable items.
:return: The same collection ordered in ascending order.

Time Complexity: O(n log n)
Space Complexity: O(n)
Sorts a list in ascending order using the merge sort algorithm.

Examples:
>>> merge_sort([0, 5, 3, 2, 2])
Expand All @@ -28,37 +10,41 @@ def merge_sort(collection: list) -> list:
>>> merge_sort([-2, -5, -45])
[-45, -5, -2]
"""
if len(collection) <= 1:
return collection

def merge(left: list, right: list) -> list:
"""
Merge two sorted lists into a single sorted list.
mid = len(collection) // 2
left = merge_sort(collection[:mid])
right = merge_sort(collection[mid:])

:param left: Left collection
:param right: Right collection
:return: Merged result
"""
result = []
while left and right:
result.append(left.pop(0) if left[0] <= right[0] else right.pop(0))
result.extend(left)
result.extend(right)
return result
return _merge(left, right)

if len(collection) <= 1:
return collection
mid_index = len(collection) // 2
return merge(merge_sort(collection[:mid_index]), merge_sort(collection[mid_index:]))

def _merge(left: list[int], right: list[int]) -> list[int]:
"""Merge two sorted lists into one sorted list."""
result = []
i = j = 0

if __name__ == "__main__":
import doctest
while i < len(left) and j < len(right):
if 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

doctest.testmod()

try:
user_input = input("Enter numbers separated by a comma:\n").strip()
unsorted = [int(item) for item in user_input.split(",")]
sorted_list = merge_sort(unsorted)
print(*sorted_list, sep=",")
except ValueError:
print("Invalid input. Please enter valid integers separated by commas.")
def get_user_input() -> list[int]:
"""Helper function to get list from user input."""
user_input = input("Enter numbers separated by a comma:\n").strip()
return [int(item) for item in user_input.split(",")]


if __name__ == "__main__":
unsorted = get_user_input()
sorted_list = merge_sort(unsorted)
print("Sorted List:", sorted_list)
Loading
Loading