3.1 Теория сортировки¶
Сортировка — одна из фундаментальных задач алгоритмики. Во многих задачах данные гораздо проще анализировать, если они расположены в упорядоченном виде.
Например:
- Проверить, есть ли в массиве одинаковые элементы — после сортировки они окажутся рядом.
- Найти самый частый элемент — одинаковые значения идут подряд.
- Найти медиану — достаточно взять элемент посередине отсортированного массива.
В этом разделе мы рассмотрим:
- простые алгоритмы сортировки O(n²),
- эффективные алгоритмы O(n log n),
- нижнюю границу для сортировки,
- линейную сортировку при специальных условиях.
Базовая задача¶
Задача: Дан массив из n элементов. Нужно отсортировать его по неубыванию.
Пример:
4 7 1 9 3 7 2
После сортировки:
1 2 3 4 7 7 9
Алгоритмы O(n²)¶
Простейшие алгоритмы сортировки содержат два вложенных цикла. Их сложность — O(n²).
Сортировка пузырьком (Bubble Sort)¶
Идея: многократно проходить по массиву и менять соседние элементы местами, если они стоят в неправильном порядке.
for (int i = 0; i < n; i++) {
for (int j = 0; j < n-1; j++) {
if (a[j] > a[j+1]) {
swap(a[j], a[j+1]);
}
}
}
После первого прохода самый большой элемент окажется в конце массива. После второго — второй по величине. И так далее.
Инверсии¶
Инверсия — это пара индексов (i, j), где:
- i < j
- a[i] > a[j]
То есть элементы стоят в неправильном порядке.
Пример:
2 5 3 4 1
Чем больше инверсий, тем дальше массив от отсортированного состояния.
В массиве, упорядоченном в обратном порядке, число инверсий равно:
n(n-1)/2 = O(n²)
Каждый обмен соседних элементов в пузырьковой сортировке устраняет ровно одну инверсию. Поэтому в худшем случае требуется O(n²) операций.
Алгоритмы O(n log n)¶
Чтобы сортировать быстрее, применяются алгоритмы "разделяй и властвуй".
Сортировка слиянием (Merge Sort)¶
Алгоритм:
- Разделить массив пополам.
- Рекурсивно отсортировать каждую половину.
- Слить две отсортированные части.
Пример:
4 7 1 9 3 7 2 6
После разделения и сортировки половин:
1 4 7 9 | 2 3 6 7
После слияния:
1 2 3 4 6 7 7 9
Почему O(n log n)?¶
- Глубина рекурсии — log n.
- На каждом уровне выполняется O(n) работы.
Итоговая сложность — O(n log n).
Нижняя граница сортировки¶
Если алгоритм основан только на сравнении элементов, то быстрее O(n log n) сортировать невозможно.
Причина: существует n! различных перестановок массива. Чтобы различить их, требуется минимум log₂(n!) сравнений.
Асимптотически:
log(n!) ≈ n log n
Следовательно, нижняя граница для сортировок на основе сравнений — Ω(n log n).
Линейная сортировка: Counting Sort¶
Если элементы — целые числа из диапазона 0…C, и C = O(n), можно отсортировать массив за O(n).
Идея¶
- Создать массив частот.
- Подсчитать, сколько раз встречается каждое число.
- Восстановить отсортированный массив.
Пример:
2 5 3 2 8 5 2
Массив частот:
число: 0 1 2 3 4 5 6 7 8 частота: 0 0 3 1 0 2 0 0 1
Результат:
2 2 2 3 5 5 8
Сложность — O(n).
Ограничение: диапазон значений должен быть небольшим.
Выводы¶
- Простые сортировки работают за O(n²).
- Эффективные сортировки сравнениями работают за O(n log n).
- Быстрее сортировать можно только при использовании дополнительной информации о данных.
- В соревнованиях обычно используется стандартная сортировка (например, std::sort), реализующая алгоритм O(n log n).
В следующем разделе мы рассмотрим практическое использование сортировки в C++.