Сортировки слиянием и быстрая (Хоара)
Ключевые тезисы
- Сортировка слиянием (merge sort) требует дополнительной памяти для слияния отсортированных массивов.
- Быстрая сортировка (quick sort) может быть реализована на месте (in-place), но в лекции представлен простой вариант с использованием дополнительных списков.
- Устойчивость сортировки — свойство не менять порядок равных элементов.
- Сортировка массива позволяет эффективно искать в нём элементы с помощью бинарного поиска.
Сортировка слиянием (Merge Sort)
Слияние отсортированных массивов
Функция merge(a, b) принимает два отсортированных списка и возвращает новый отсортированный список c.
Алгоритм слияния:
- Создать список
cдлинойlen(a) + len(b). - Инициализировать индексы
i,k,n(дляa,b,c) нулями. - Пока оба индекса
iиkне вышли за границы своих массивов:- Сравнить
a[i]иb[k]. - Для обеспечения устойчивости сортировки, если
a[i] <= b[k], скопироватьa[i]вc[n]. Иначе скопироватьb[k]. - Увеличить индекс (
iилиk) и индексnна 1.
- Сравнить
- Когда один из массивов исчерпан, скопировать оставшиеся элементы из второго массива в
c.
Рекурсивная сортировка
Функция merge_sort(a):
- Крайний случай: если длина
a<= 1, массив считается отсортированным. - Найти середину массива (
mid). - Рекурсивно вызвать
merge_sortдля левой (a[:mid]) и правой (a[mid:]) половин. - Слить отсортированные половины с помощью
merge. - Важно: результат слияния (
c) нужно поэлементно скопировать обратно в исходный массивa.
Примечание: Для экономии памяти можно реализовать слияние, работающее непосредственно с частями исходного массива, используя индексы.
Быстрая сортировка (Quick Sort)
Алгоритм (простая реализация с дополнительной памятью)
Функция quick_sort(a):
- Крайний случай: если длина
a<= 1, завершить работу. - Выбрать барьерный элемент (в примере — первый элемент массива,
pivot = a[0]). - Создать три пустых списка:
L(элементы < pivot),M(элементы == pivot),R(элементы > pivot). - Пройти по всем элементам массива
aи распределить их по спискамL,M,R. - Рекурсивно вызвать
quick_sortдляLиR. - Склеить (переписать) отсортированные
L, затемM, затемRобратно в массивa.
Примечание: Более эффективная реализация работает на месте, без создания дополнительных списков, используя индексы и перестановки элементов.
Проверка упорядоченности массива
Функция check_sorted(a, ascending=True) проверяет, отсортирован ли массив по возрастанию или убыванию.
- Проходит по массиву, сравнивая соседние элементы.
- Если встречается пара элементов, нарушающая заданный порядок, возвращает
False. - Параметр
ascendingопределяет направление проверки (по умолчанию — по возрастанию).
Логика сравнения: Для универсальности используется коэффициент s = 2 * int(ascending) - 1, который преобразует True в 1, а False в -1. Условие проверки: s * a[i] > s * a[i+1].
Бинарный поиск в отсортированном массиве
Зачем сортировать? Чтобы искать элементы не линейным перебором (O(n)), а бинарным поиском (O(log n)).
Идея алгоритма:
- Определяются границы поиска:
left(левая) иright(правая). - На каждой итерации вычисляется середина:
middle = (left + right) // 2. - Элемент
a[middle]сравнивается с искомым значением. - В зависимости от результата сравнения границы поиска сужаются: отбрасывается левая или правая половина текущего интервала.
Важные варианты поиска:
- Поиск левой границы: находит индекс первого вхождения элемента (или индекс элемента, меньшего искомого, если его нет).
- Поиск правой границы: находит индекс последнего вхождения элемента (или индекс элемента, большего искомого, если его нет).
Граничные значения:
leftне может быть меньше -1,rightне может быть больше длины массиваn.