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
78 changes: 78 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# ruff.toml
target-version = "py311"
line-length = 100

# включить все основные правила
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # Pyflakes
"I", # isort
"N", # pep8-naming
"UP", # pyupgrade
"YTT", # flake8-2020
"S", # flake8-bandit
"A", # flake8-builtins
"COM", # flake8-commas
"C4", # flake8-comprehensions
"DTZ", # flake8-datetimez
"T10", # flake8-debugger
"EM", # flake8-errmsg
"EXE", # flake8-executable
"ISC", # flake8-implicit-str-concat
"ICN", # flake8-import-conventions
"G", # flake8-logging-format
"INP", # flake8-no-pep420
"PIE", # flake8-pie
"T20", # flake8-print
"PYI", # flake8-pyi
"PT", # flake8-pytest-style
"Q", # flake8-quotes
"RSE", # flake8-raise
"RET", # flake8-return
"SLF", # flake8-self
"SIM", # flake8-simplify
"TID", # flake8-tidy-imports
"TCH", # flake8-type-checking
"INT", # flake8-gettext
"ARG", # flake8-unused-arguments
"FBT", # flake8-boolean-trap
"B", # flake8-bugbear
"AIR", # flake8-airflow
"PERF", # flake8-perflint
]

# игнорировать правила
ignore = [
"E501", # line too long - handled by formatter
"S101", # assert used - ok in tests
"T201", # print found - sometimes needed
"COM812", # trailing comma missing - not always required
]

# настройки для конкретных файлов
[per-file-ignores]
"__init__.py" = ["F401"] # Unused imports allowed in __init__.py
"tests/**" = ["S101", "SLF001"] # Allow assert and self in tests
"**/migrations/**" = ["ALL"] # Ignore all in migrations

# настройки форматтера
[format]
indent-style = "space"
quote-style = "double"
skip-magic-trailing-comma = false
line-ending = "auto"

# настройки для конкретных правил
[flake8-quotes]
docstring-quotes = "double"
inline-quotes = "double"

[flake8-tidy-imports]
ban-relative-imports = "all"

[isort]
known-first-party = ["myapp"]
lines-after-imports = 2
combine-as-imports = true
split-on-trailing-comma = true
53 changes: 53 additions & 0 deletions src/hw_6_1_sort_with_test/hw_6_1_sort_with_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# преобразование в двоичную кучу с корнем i (индекс в arr); n - размер кучи
def heapify(arr, n, i):
largest = i
l = 2 * i + 1 # left
r = 2 * i + 2 # right

# проверяем существует ли левый дочерний элемент больший, чем корень
if l < n and arr[i] < arr[l]:
largest = l

# проверяем существует ли правый дочерний элемент больший, чем корень
if r < n and arr[largest] < arr[r]:
largest = r

# заменяем корень, если нашёлся элемент больше
if largest != i:
arr[i], arr[largest] = arr[largest], arr[i]

# применяем heapify к корню
heapify(arr, n, largest)


# сортировка массива (основная функция)
def heap_sort(arr):
n = len(arr)

# строим max-heap
for i in range(n, -1, -1):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Нет смысла начинать с n, эффективнее начинать с последнего родительского узла n // 2 - 1.

heapify(arr, n, i)

# переворачиваем массив
for i in range(n - 1, 0, -1):
arr[i], arr[0] = arr[0], arr[i] # свап
heapify(arr, i, 0)

return arr

#
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Чтобы не комментировать такие вещи, оборачивайте то, что должно запускаться только при запуске непосредственно самого файла, в if __name__ == "__main__".

# data = []
# n = int(input('Введите количество элементов: '))
#
# print(f'Введите {n} элементов')
# for i in range(n):
# try:
# current = int(input())
# except ValueError:
# print("Wrong input!")
# current = int(input())
#
# data.append(current)
#
# heap_sort(data)
# print(data)
10 changes: 10 additions & 0 deletions src/hw_6_1_sort_with_test/instruction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Инструкция по использованию тестов

Домашнее задание 6.1 Heap sort

1. Откройте терминал и перейдите в директорию с файлами приложения (hw_6_1_sort_with_test)
2. Выполните команду запуска тестов:
```console
pytest test_hw_6_1_sort_with_test.py
```
3. Смотрите результат
116 changes: 116 additions & 0 deletions src/hw_6_1_sort_with_test/test_hw_6_1_sort_with_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
from hw_6_1_sort_with_test import heap_sort
from random import randint, uniform
import pytest

"""Обычные тесты"""


@pytest.mark.parametrize(
["n"], [(([randint(0, 300) for _ in range(10)]),) for _ in range(10)]
)
def test_heap_sort_positive(n):
"""Тест с неотрицательными числами"""
assert heap_sort(n) == sorted(n)


@pytest.mark.parametrize(
["n"], [(([randint(-300, -1) for _ in range(10)]),) for _ in range(10)]
)
def test_heap_sort_negative(n):
"""Тест с отрицательными числами"""
assert heap_sort(n) == sorted(n)


@pytest.mark.parametrize(
["n"], [(([uniform(-50, 50) for _ in range(10)]),) for _ in range(10)]
)
def test_float_numbers(n):
"""Тест с числами с плавающей точкой"""
assert heap_sort(n) == sorted(n)


"""Тесты крайних случаев"""


def test_empty_list():
"""Тест пустого списка"""
assert heap_sort([]) == []


def test_single_element():
"""Тест одного элемента"""
assert heap_sort([5]) == [5]


def test_large_random_list():
"""Тест большого случайного списка"""
test_list = [randint(-1000, 1000) for _ in range(1000)]
assert heap_sort(test_list) == sorted(test_list)


def test_very_large_numbers():
"""Тест с очень большими числами"""
assert heap_sort([2 ** 100, 0, -1, -2 ** 11]) == [-2 ** 11, -1, 0, 2 ** 100]


def test_already_sorted():
"""Тест уже отсортированного массива"""
assert heap_sort([1, 2, 3, 4, 5]) == [1, 2, 3, 4, 5]


def test_duplicates():
"""Тест с дубликатами"""
assert heap_sort([3, 1, 4, 1, 5, 9, 2, 6, 5]) == [1, 1, 2, 3, 4, 5, 5, 6, 9]


"""Некоторые сортировки для сравнения"""


def bubble_sort(arr):
"""Пузырьковая сортировка для сравнения"""
if not arr:
return arr.copy()
result = arr.copy()
n = len(result)
for i in range(n):
for j in range(0, n - i - 1):
if result[j] > result[j + 1]:
result[j], result[j + 1] = result[j + 1], result[j]
return result


def selection_sort(arr):
"""Сортировка выбором для сравнения"""
if not arr:
return arr.copy()
result = arr.copy()
n = len(result)
for i in range(n):
min_idx = i
for j in range(i + 1, n):
if result[j] < result[min_idx]:
min_idx = j
result[i], result[min_idx] = result[min_idx], result[i]
return result


"""Сравнение с другими сортировками"""


def test_heap_sort_builtin():
"""Сравнение с встроенной сортировкой"""
n = [1, 2, 10, 1, 100]
assert heap_sort(n) == sorted(n)


def test_compare_with_bubble_sort():
"""Сравнение с пузырьковой сортировкой"""
n = [1, 2, 10, 1, 100]

assert heap_sort(n) == bubble_sort(n)


def test_compare_with_selection_sort():
"""Сравнение с сортировкой выбором"""
n = [1, 2, 10, 1, 100]
assert heap_sort(n) == selection_sort(n)
Comment on lines +100 to +116
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Нужно использовать .copy() так как вы передаете один и тот же массив.

Comment on lines +97 to +116
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Для property based тестов нужно тоже генерировать массивы и сравнивать на них сортировки.