1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/wizardforcel-lmpythw-zh

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
ex16.md 20 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 06.03.2025 03:52 f9ec0dd

Упражнение 16: сортировка пузырьком, быстрая сортировка и сортировка слиянием

Оригинал: Exercise OnClickListener

Переводчик: Феликс Ли

Лицензия: CC BY-NC-SA 4.0

Гордо использует Google Translate

Теперь вы попробуете реализовать алгоритмы сортировки для вашего DoubleLinkedList данных. Для этих описаний я буду использовать "числовой список" для представления случайного списка объектов. Это может быть колода карт, список чисел на бумаге, список имён или что угодно другое, что можно отсортировать. Когда вы пытаетесь отсортировать числовой список, обычно есть три варианта:

Сортировка пузырьком

Если вы ничего не знаете о сортировках, это наиболее вероятный вариант, который вы попробуете. Он просто включает прохождение по списку и обмен любых неправильно расположенных пар. Вы продолжаете проходить по списку, менять местами пары до тех пор, пока не перестанете делать какие-либо замены. Легко понять, но особенно медленный.

Быстрая сортировка

Этот метод также известен как "сортировка Хоара". Она работает путём выбора опорного элемента, разделения списка на две части — элементы меньше опорного и элементы больше опорного, а затем рекурсивной сортировки каждой части. Быстрая сортировка эффективна и часто используется благодаря своей скорости.

Сортировка слиянием

Этот метод сортировки работает путём деления списка на более мелкие части, сортировки этих частей и последующего объединения их обратно в один отсортированный список. Этот подход обеспечивает стабильность и эффективность при работе с большими данными.> Этот алгоритм сортировки делит список пополам, затем на четыре части, пока он больше не может быть разделён. Затем он объединяет эти части обратно, но при этом проверяет порядок каждой части, чтобы правильно выполнить операцию. Это умный алгоритм, хорошо работающий со связными списками, но не очень эффективный для массивов фиксированной размерности, так как вам нужна некоторая очередь для отслеживания частей.> Быстрая сортировка

Это похоже на сортировку слиянием, потому что это "разделяй и властвуй" алгоритм, но принцип заключается в том, чтобы менять местами элементы вокруг точки разделения, а не разделять и объединять список. В самом простом виде вы выбираете диапазон от нижней границы до верхней и точку разделения. Затем меняете местами элементы выше точки разделения, если они больше её, и ниже, если меньше. Затем выбираете новую нижнюю границу, верхнюю и точку разделения, находящиеся внутри нового неотсортированного списка, и повторяете процесс. Она делит список на меньшие блоки, но не разделяет его таким образом, как сортировка слиянием.

Часто задаваемые вопросыЦель этого упражнения — научиться реализовывать алгоритмы на основе описаний "ложного кода" или "pseudo code". Вы будете исследовать алгоритмы с помощью источников, которые я предоставлю (в основном Википедия), а затем реализуете их на основе псевдокода. В видео этого упражнения я быстро завершу первые два, более детальные вещи оставлены за упражнением. Так что ваша задача — самостоятельно реализовать алгоритм быстрой сортировки. Сначала мы посмотрим описание сортировки пузырьком на странице Википедии сортировка пузырьком:

процедура bubbleSort(A : список сортируемых элементов)
    n = длина(A)
    повторять
        swapped = false
        для i = 1 до n-1 включительно делать
            /* Если эта пара элементов расположена неправильно */
            если A[i-1] > A[i] то
                /* Обменять их местами и помнить об этом */
                swap(A[i-1], A[i])
                swapped = true
            конец если
        конец для
    пока swapped
конец процедуры
```Вы заметите, что псевдокод представляет собой лишь приближенное описание алгоритма, которое может сильно различаться от одного источника к другому — книг, авторов или страниц Википедии. Он предполагает, что вы можете прочитать этот "контур" программы и адаптировать его под свои нужды. Иногда это может напоминать старый язык программирования Algol, а иногда выглядеть как некорректно оформленный JavaScript или Python. Вам просто нужно догадываться о значении этого контура и переводить его в нужный вам формат. Вот мой начальный перевод данного конкретного псевдокода:

```py
def bubble_sort(numbers):
    """Сортирует список чисел методом пузырьковой сортировки."""
    while True:
        # Начнём с предположения, что список уже отсортирован
        is_sorted = True
        # Будем сравнивать пары элементов, пропустив первый элемент
        node = numbers.begin.next
        while node:
            # Пройдёмся по списку и сравним текущий элемент со следующим
            if node.prev.value > node.value:
                # Если предыдущий элемент больше, меняем их местами
                node.prev.value, node.value = node.value, node.prev.value
                # Это значит, что нам потребуется ещё один проход по списку
                is_sorted = False
            node = node.next

        # Если мы сделали полный проход по списку и ничего не поменяли,
        # значит он уже отсортирован
        if is_sorted: break
```Я добавил эти комментарии, чтобы вы могли учиться и следовать за процессом. Сравните, что я сделал здесь, с исходным псевдокодом. Также обратите внимание, что структура данных, используемая на страницах Википедии, отличается от `DoubleLinkedList`. Код Википедии предполагает работу с массивами или списками. Вам следует заменить эту строчку:

```python
if A[i-1] > A[i] then

на использование DoubleLinkedList, переведенное на Python:

if node.prev.value > node.value:

Мы не можем легко случайно обращаться к элементам DoubleLinkedList, поэтому нам нужно преобразовать операции с индексами массива в .next и .prev. В цикле также важно проверять, являются ли свойства next или prev равными None. Этот переход требует много времени для перевода, обучения и догадок относительно того, что именно имеется в виду в данном псевдокоде.

Изучение сортировки пузырьком

Теперь вы должны потратить время на изучение этого Python-кода bubble_sort, чтобы понять, как я его реализовал. Убедитесь, что вы просмотрели моё видео в реальном времени и получили больше контекста. Вы также должны создать графики выполнения алгоритма на различных типах списков (отсортированных, случайных, с повторами и т.д.). Как только вы поймёте, как это сделано, проведите исследование pytest и алгоритма merge_sort:

import sorting
from dllist import DoubleLinkedList
from random import randint
``````markdown
max_numbers = 30

def random_list(count):
    numbers = DoubleLinkedList()
    for i in range(count, 0, -1):
        numbers.shift(randint(0, 10000))
    return numbers


def is_sorted(numbers):
    node = numbers.begin
    while node and node.next:
        if node.value > node.next.value:
            return False
        else:
            node = node.next

    return True


def test_bubble_sort():
    numbers = random_list(max_numbers)

    sorting.bubble_sort(numbers)

    assert is_sorted(numbers)


def test_merge_sort():
    numbers = random_list(max_numbers)

    sorting.merge_sort(numbers)

    assert is_sorted(numbers)


Этот тестовый код включает важную часть, где используется функция `random.randint` для генерации случайных данных для тестирования. Этот тест не проверяет многие граничные случаи, но это начало, которое мы будем улучшать впоследствии. Вспомните, что вы еще не реализовали `sort.merge_sort`, поэтому вы можете не писать этот тестовый метод или закомментировать его.

Как только вы завершите тестирование и напишете этот код, снова изучите страницу Википедии, а затем попробуйте несколько других версий `bubble_sort` перед тем, как пробовать `merge_sort`.

### Слиянием сортировка

Я пока не готов был вас попросить самостоятельно её реализовать. Я повторю этот процесс для функции `merge_sort`, но на этот раз я хочу, чтобы вы попробовали реализовать алгоритм слиянием сортировки, используя псевдокод с страницы Википедии. После этого вы сможете сравнить вашу реализацию с моей.

Несколько реализаций предлагается, но я использую "сверху вниз" версию:
``````py
def merge_sort(m):
    if len(m) <= 1:
        return m

    left = []
    right = []

    for i, x in enumerate(m):
        if i < len(m) / 2:
            left.append(x)
        else:
            right.append(x)

    left = merge_sort(left)
    right = merge_sort(right)

    return merge(left, right)


def merge(left, right):
    result = []

    while left and right:
        if left[0] <= right[0]:
            result.append(left[0])
            left = left[1:]
        else:
            result.append(right[0])
            right = right[1:]

    while left:
        result.append(left[0])
        left = left[1:]

    while right:
        result.append(right[0])
        right = right[1:]

    return result


# Для test_merge_sort напишите оставшиеся тестовые случаи функции, а затем попробуйте её реализацию.
# Вам может потребоваться способ вычисления количества узлов от данного узла.
# Это то, что DoubleLinkedList не делает.


def count(node):
    count = 0

    while node:
        node = node.next
        count += 1

    return count
def сортировка_слиянием(числа):
    числа.begin = слияние_узел(числа.begin)

    # ужасный способ получить конец
    узел = числа.begin
    while узел.next:
        узел = узел.next
    числа.end = узел


def слияние_узел(начало):
    """Сортирует список чисел методом сортировки слиянием."""
    if начало.next == None:
        return начало

    средняя = len(начало) // 2

    # сканирование до середины
    сканер = начало
    for i in range(0, средняя - 1):
        сканер = сканер.next

    # установка узла середины сразу после точки сканирования
    средний_узел = сканер.next
    # разрыв в точке середины
    сканер.next = None
    средний_узел.prev = None

    слева = слияние_узел(начало)
    справа = слияние_узел(средний_узел)

    return слияние(слева, справа)


def слияние(слева, справа):
    """Выполняет слияние двух списков."""
    результат = None

    if слева == None: 
        return справа
    if справа == None: 
        return слева

    if значение(слева) > значение(права): 
        результат = справа
        результат.next = слияние(слева, справа.next)
    else:
        результат = слева
        результат.next = слияние(слева.next, справа)

    результат.next.prev = результат
    return результат

При попытке реализовать это, я буду использовать этот код как "памятку", чтобы быстро получить подсказки. Вы также заметите, что я пытаюсь полностью воспроизвести этот код из видео, поэтому вы можете видеть, как я стараюсь решить те же проблемы, с которыми вы могли столкнуться.### Быстрая сортировка ```Наконец, попробуйте реализовать quick_sort и создайте тестовый случай `test_quicksort`. Я рекомендую вам начать с простой реализации быстрой сортировки с использованием обычных списков в Python. Это поможет лучше понять алгоритм. Затем используйте простой Python-код, чтобы сделать его способным работать с головным узлом `DoubleLinkedList`. Помните, что стоит потратить достаточно времени на это, а также провести множество тестов и отладочных операций в `test_quicksort`.

Глубокое изучение+ Эти реализации точно не являются самыми эффективными по производительности. Попробуй написать несколько сумасшедших тестов, чтобы показать это. Возможно, тебе потребуется передать большой список в алгоритм. Используй свои исследования, чтобы найти патологические (абсолютно худшие) случаи. Например, что произойдет, если ты передашь отсортированный список методу quick_sort?

  • Не реализуй никаких улучшений, но исследуй различные способы улучшения этих алгоритмов.
  • Найди другие сортировочные алгоритмы и попробуй их реализовать.
  • Они могут работать и на односвязных списках? На очередях и стеках? Будут ли они полезны?
  • Изучи теоретическую скорость этих алгоритмов. Ты увидишь упоминание O(n^2) или O(n log n), что указывает на то, что эти алгоритмы имеют плохие временные характеристики в худшем случае. Определение "большого O" для алгоритма выходит за рамки этой книги, но мы кратко обсудим эти метрики в упражнении 18.
  • Я реализовал эти алгоритмы как отдельные модули, но будет ли проще добавить их как функции в DoubleLinkedList? Если ты сделаешь так, тебе придется скопировать этот код для других управляемых данными структур? У нас нет такого дизайна, который позволил бы этим сортировочным алгоритмам работать со всеми "похожими на связный список" структурами данных.
  • Больше никогда не используй пузырьковую сортировку.Я включил её здесь, потому что часто встречаются плохие примеры кода, и мы увеличим её производительность в упражнении 19.

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/wizardforcel-lmpythw-zh.git
git@api.gitlife.ru:oschina-mirror/wizardforcel-lmpythw-zh.git
oschina-mirror
wizardforcel-lmpythw-zh
wizardforcel-lmpythw-zh
master