diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 94c0218..540c14c 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -11,7 +11,8 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.9", "3.10"] + python-version: [3.9, 3.11, 3.12] + steps: - uses: actions/checkout@v4 diff --git a/src/sorting_algorithms/__init__.py b/src/sorting_algorithms/__init__.py new file mode 100644 index 0000000..ede1d06 --- /dev/null +++ b/src/sorting_algorithms/__init__.py @@ -0,0 +1,6 @@ +from .bubble_sort import bubble_sort +from .quick_sort import quick_sort +from .heap_sort import heap_sort +from .merge_sort import merge_sort + +__all__ = ['bubble_sort', 'quick_sort', 'heap_sort', 'merge_sort'] diff --git a/src/sorting_algorithms/bubble_sort.py b/src/sorting_algorithms/bubble_sort.py new file mode 100644 index 0000000..968bb51 --- /dev/null +++ b/src/sorting_algorithms/bubble_sort.py @@ -0,0 +1,9 @@ +def bubble_sort(arr): + arr_copy = arr.copy() + n = len(arr_copy) + for i in range(n): + for j in range(0, n-i-1): + if arr_copy[j] > arr_copy[j+1]: + arr_copy[j], arr_copy[j+1] = arr_copy[j+1], arr_copy[j] + return arr_copy + diff --git a/src/sorting_algorithms/heap_sort.py b/src/sorting_algorithms/heap_sort.py new file mode 100644 index 0000000..f58fe13 --- /dev/null +++ b/src/sorting_algorithms/heap_sort.py @@ -0,0 +1,38 @@ +def heap_sort(arr): + """ + Сортировка кучей + """ + n = len(arr) + + # Строим кучу + for i in range(n // 2 - 1, -1, -1): + heap(arr, n, i) + + # Извлекаем элементы + for i in range(n - 1, 0, -1): + arr[0], arr[i] = arr[i], arr[0] # Меняем корень с последним + heap(arr, i, 0) # Просеиваем новый корень + + return arr + + +def heap(arr, n, i): + """ + Просеивание + """ + largest = i # Считаем текущий элемент наибольшим + left = 2 * i + 1 # Левый ребенок + right = 2 * i + 2 # Правый ребенок + + # Если левый ребенок существует и больше родителя + if left < n and arr[left] > arr[largest]: + largest = left + + # Если правый ребенок существует и больше наибольшего + if right < n and arr[right] > arr[largest]: + largest = right + + # Если наибольший элемент не родитель + if largest != i: + arr[i], arr[largest] = arr[largest], arr[i] # Меняем местами + heap(arr, n, largest) # Просеиваем дальше diff --git a/src/sorting_algorithms/merge_sort.py b/src/sorting_algorithms/merge_sort.py new file mode 100644 index 0000000..f860aaa --- /dev/null +++ b/src/sorting_algorithms/merge_sort.py @@ -0,0 +1,44 @@ +def merge_sort(arr): + """ + Сортировка слиянием + """ + if len(arr) <= 1: + return arr.copy() + + arr_copy = arr.copy() + _merge_sort_helper(arr_copy) + return arr_copy + +def _merge_sort_helper(arr): + if len(arr) > 1: + mid = len(arr) // 2 + left_half = arr[:mid] + right_half = arr[mid:] + + # Рекурсивная сортировка обеих половин + _merge_sort_helper(left_half) + _merge_sort_helper(right_half) + + i = j = k = 0 + + # Слияние отсортированных половин + while i < len(left_half) and j < len(right_half): + if left_half[i] < right_half[j]: + arr[k] = left_half[i] + i += 1 + else: + arr[k] = right_half[j] + j += 1 + k += 1 + + # Проверяем, не остались ли элементы в левой половине + while i < len(left_half): + arr[k] = left_half[i] + i += 1 + k += 1 + + # Проверяем, не остались ли элементы в правой половине + while j < len(right_half): + arr[k] = right_half[j] + j += 1 + k += 1 diff --git a/src/sorting_algorithms/quick_sort.py b/src/sorting_algorithms/quick_sort.py new file mode 100644 index 0000000..bf4a175 --- /dev/null +++ b/src/sorting_algorithms/quick_sort.py @@ -0,0 +1,36 @@ +def quick_sort(arr): + """ + Быстрая сортировка + """ + if len(arr) <= 1: + return arr + + arr_copy = arr.copy() + _quick_sort_helper(arr_copy, 0, len(arr_copy) - 1) + return arr_copy + +def _quick_sort_helper(arr, low, high): + if low < high: + # Индекс разделения + pi = _partition(arr, low, high) + + # Рекурсивно сортируем элементы до и после разделения + _quick_sort_helper(arr, low, pi - 1) + _quick_sort_helper(arr, pi + 1, high) + +def _partition(arr, low, high): + # Выбираем последний элемент как опорный + pivot = arr[high] + + # Индекс меньшего элемента (указывает на правильную позицию pivot) + i = low - 1 + + for j in range(low, high): + # Если текущий элемент меньше или равен опорному + if arr[j] <= pivot: + i += 1 + arr[i], arr[j] = arr[j], arr[i] + + # Помещаем опорный элемент в правильную позицию + arr[i + 1], arr[high] = arr[high], arr[i + 1] + return i + 1 diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_bubble_sort.py b/tests/test_bubble_sort.py new file mode 100644 index 0000000..c5767e6 --- /dev/null +++ b/tests/test_bubble_sort.py @@ -0,0 +1,22 @@ +from src.sorting_algorithms import bubble_sort + +def test_empty_list(): + assert bubble_sort([]) == [] + +def test_single_element(): + assert bubble_sort([5]) == [5] + +def test_sorted_list(): + assert bubble_sort([1, 2, 3, 4, 5]) == [1, 2, 3, 4, 5] + +def test_reverse_sorted(): + assert bubble_sort([5, 4, 3, 2, 1]) == [1, 2, 3, 4, 5] + +def test_duplicate_elements(): + assert bubble_sort([3, 1, 4, 1, 5, 9, 2, 6, 5]) == [1, 1, 2, 3, 4, 5, 5, 6, 9] + +def test_negative_numbers(): + assert bubble_sort([-3, -1, -4, -2]) == [-4, -3, -2, -1] + +def test_mixed_numbers(): + assert bubble_sort([3, -1, 0, -2, 5]) == [-2, -1, 0, 3, 5] diff --git a/tests/test_heap_sort.py b/tests/test_heap_sort.py new file mode 100644 index 0000000..db28347 --- /dev/null +++ b/tests/test_heap_sort.py @@ -0,0 +1,23 @@ +from src.sorting_algorithms import heap_sort + +def test_empty_list(): + assert heap_sort([]) == [] + +def test_single_element(): + assert heap_sort([5]) == [5] + +def test_sorted_list(): + assert heap_sort([1, 2, 3, 4, 5]) == [1, 2, 3, 4, 5] + +def test_reverse_sorted(): + assert heap_sort([5, 4, 3, 2, 1]) == [1, 2, 3, 4, 5] + +def test_duplicate_elements(): + assert heap_sort([3, 1, 4, 1, 5, 9, 2, 6, 5]) == [1, 1, 2, 3, 4, 5, 5, 6, 9] + +def test_negative_numbers(): + assert heap_sort([-3, -1, -4, -2]) == [-4, -3, -2, -1] + +def test_mixed_numbers(): + assert heap_sort([3, -1, 0, -2, 5]) == [-2, -1, 0, 3, 5] + diff --git a/tests/test_merge_sort.py b/tests/test_merge_sort.py new file mode 100644 index 0000000..c09fa2d --- /dev/null +++ b/tests/test_merge_sort.py @@ -0,0 +1,23 @@ +from src.sorting_algorithms import merge_sort + +def test_empty_list(): + assert merge_sort([]) == [] + +def test_single_element(): + assert merge_sort([5]) == [5] + +def test_sorted_list(): + assert merge_sort([1, 2, 3, 4, 5]) == [1, 2, 3, 4, 5] + +def test_reverse_sorted(): + assert merge_sort([5, 4, 3, 2, 1]) == [1, 2, 3, 4, 5] + +def test_duplicate_elements(): + assert merge_sort([3, 1, 4, 1, 5, 9, 2, 6, 5]) == [1, 1, 2, 3, 4, 5, 5, 6, 9] + +def test_negative_numbers(): + assert merge_sort([-3, -1, -4, -2]) == [-4, -3, -2, -1] + +def test_mixed_numbers(): + assert merge_sort([3, -1, 0, -2, 5]) == [-2, -1, 0, 3, 5] + diff --git a/tests/test_quick_sort.py b/tests/test_quick_sort.py new file mode 100644 index 0000000..36d72b8 --- /dev/null +++ b/tests/test_quick_sort.py @@ -0,0 +1,23 @@ +from src.sorting_algorithms import quick_sort + +def test_empty_list(): + assert quick_sort([]) == [] + +def test_single_element(): + assert quick_sort([5]) == [5] + +def test_sorted_list(): + assert quick_sort([1, 2, 3, 4, 5]) == [1, 2, 3, 4, 5] + +def test_reverse_sorted(): + assert quick_sort([5, 4, 3, 2, 1]) == [1, 2, 3, 4, 5] + +def test_duplicate_elements(): + assert quick_sort([3, 1, 4, 1, 5, 9, 2, 6, 5]) == [1, 1, 2, 3, 4, 5, 5, 6, 9] + +def test_negative_numbers(): + assert quick_sort([-3, -1, -4, -2]) == [-4, -3, -2, -1] + +def test_mixed_numbers(): + assert quick_sort([3, -1, 0, -2, 5]) == [-2, -1, 0, 3, 5] +